enhance reduce_vars (#1814)
- allow immediate assignment after declaration of variable - relax modification rule for immutable value - fix order of visit for TreeWalker - remove extraneous code
This commit is contained in:
12
lib/ast.js
12
lib/ast.js
@@ -182,21 +182,13 @@ var AST_BlockStatement = DEFNODE("BlockStatement", null, {
|
|||||||
}, AST_Block);
|
}, AST_Block);
|
||||||
|
|
||||||
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
||||||
$documentation: "The empty statement (empty block or simply a semicolon)",
|
$documentation: "The empty statement (empty block or simply a semicolon)"
|
||||||
_walk: function(visitor) {
|
|
||||||
return visitor._visit(this);
|
|
||||||
}
|
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
|
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
|
||||||
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
|
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
|
body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
|
||||||
},
|
|
||||||
_walk: function(visitor) {
|
|
||||||
return visitor._visit(this, function(){
|
|
||||||
this.body._walk(visitor);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
@@ -551,11 +543,11 @@ var AST_Call = DEFNODE("Call", "expression args", {
|
|||||||
},
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.expression._walk(visitor);
|
|
||||||
var args = this.args;
|
var args = this.args;
|
||||||
for (var i = 0, len = args.length; i < len; i++) {
|
for (var i = 0, len = args.length; i < len; i++) {
|
||||||
args[i]._walk(visitor);
|
args[i]._walk(visitor);
|
||||||
}
|
}
|
||||||
|
this.expression._walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ merge(Compressor.prototype, {
|
|||||||
option: function(key) { return this.options[key] },
|
option: function(key) { return this.options[key] },
|
||||||
compress: function(node) {
|
compress: function(node) {
|
||||||
if (this.option("expression")) {
|
if (this.option("expression")) {
|
||||||
node = node.process_expression(true);
|
node.process_expression(true);
|
||||||
}
|
}
|
||||||
var passes = +this.options.passes || 1;
|
var passes = +this.options.passes || 1;
|
||||||
for (var pass = 0; pass < passes && pass < 3; ++pass) {
|
for (var pass = 0; pass < passes && pass < 3; ++pass) {
|
||||||
@@ -128,7 +128,7 @@ merge(Compressor.prototype, {
|
|||||||
node = node.transform(this);
|
node = node.transform(this);
|
||||||
}
|
}
|
||||||
if (this.option("expression")) {
|
if (this.option("expression")) {
|
||||||
node = node.process_expression(false);
|
node.process_expression(false);
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
},
|
},
|
||||||
@@ -200,7 +200,7 @@ merge(Compressor.prototype, {
|
|||||||
return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
|
return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Node.DEFMETHOD("process_expression", function(insert, compressor) {
|
AST_Scope.DEFMETHOD("process_expression", function(insert, compressor) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var tt = new TreeTransformer(function(node) {
|
var tt = new TreeTransformer(function(node) {
|
||||||
if (insert && node instanceof AST_SimpleStatement) {
|
if (insert && node instanceof AST_SimpleStatement) {
|
||||||
@@ -244,10 +244,10 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
});
|
});
|
||||||
return self.transform(tt);
|
self.transform(tt);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
|
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) {
|
||||||
var reduce_vars = rescan && compressor.option("reduce_vars");
|
var reduce_vars = rescan && compressor.option("reduce_vars");
|
||||||
var toplevel = compressor.option("toplevel");
|
var toplevel = compressor.option("toplevel");
|
||||||
var safe_ids = Object.create(null);
|
var safe_ids = Object.create(null);
|
||||||
@@ -258,7 +258,7 @@ merge(Compressor.prototype, {
|
|||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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;
|
||||||
if (reduce_vars) {
|
if (reduce_vars) {
|
||||||
@@ -268,7 +268,7 @@ merge(Compressor.prototype, {
|
|||||||
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 || !is_safe(d)
|
||||||
|| is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) {
|
|| is_modified(node, 0, is_immutable(node.fixed_value()))) {
|
||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,6 +293,20 @@ merge(Compressor.prototype, {
|
|||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Assign
|
||||||
|
&& node.operator == "="
|
||||||
|
&& node.left instanceof AST_SymbolRef) {
|
||||||
|
var d = node.left.definition();
|
||||||
|
if (HOP(safe_ids, d.id) && d.fixed == null) {
|
||||||
|
d.fixed = function() {
|
||||||
|
return node.right;
|
||||||
|
};
|
||||||
|
mark(d, false);
|
||||||
|
node.right.walk(tw);
|
||||||
|
mark(d, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
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 || is_safe(d)) {
|
||||||
@@ -309,21 +323,24 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
var iife;
|
var iife;
|
||||||
if (node instanceof AST_Function
|
if (node instanceof AST_Function
|
||||||
&& !node.name
|
|
||||||
&& (iife = tw.parent()) instanceof AST_Call
|
&& (iife = tw.parent()) instanceof AST_Call
|
||||||
&& iife.expression === node) {
|
&& iife.expression === node) {
|
||||||
// Virtually turn IIFE parameters into variable definitions:
|
if (node.name) {
|
||||||
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
|
node.name.definition().fixed = node;
|
||||||
// So existing transformation rules can work on them.
|
} else {
|
||||||
node.argnames.forEach(function(arg, i) {
|
// Virtually turn IIFE parameters into variable definitions:
|
||||||
var d = arg.definition();
|
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
|
||||||
d.fixed = function() {
|
// So existing transformation rules can work on them.
|
||||||
return iife.args[i] || make_node(AST_Undefined, iife);
|
node.argnames.forEach(function(arg, i) {
|
||||||
};
|
var d = arg.definition();
|
||||||
mark(d, true);
|
d.fixed = function() {
|
||||||
});
|
return iife.args[i] || make_node(AST_Undefined, iife);
|
||||||
|
};
|
||||||
|
mark(d, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (node instanceof AST_If || node instanceof AST_DWLoop) {
|
if (node instanceof AST_If) {
|
||||||
node.condition.walk(tw);
|
node.condition.walk(tw);
|
||||||
push();
|
push();
|
||||||
node.body.walk(tw);
|
node.body.walk(tw);
|
||||||
@@ -335,6 +352,13 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_DWLoop) {
|
||||||
|
push();
|
||||||
|
node.condition.walk(tw);
|
||||||
|
node.body.walk(tw);
|
||||||
|
pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_LabeledStatement) {
|
if (node instanceof AST_LabeledStatement) {
|
||||||
push();
|
push();
|
||||||
node.body.walk(tw);
|
node.body.walk(tw);
|
||||||
@@ -401,13 +425,17 @@ merge(Compressor.prototype, {
|
|||||||
def.should_replace = undefined;
|
def.should_replace = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_modified(node, level, func) {
|
function is_immutable(value) {
|
||||||
|
return value && value.is_constant() || value instanceof AST_Lambda;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_modified(node, level, immutable) {
|
||||||
var parent = tw.parent(level);
|
var parent = tw.parent(level);
|
||||||
if (is_lhs(node, parent)
|
if (is_lhs(node, parent)
|
||||||
|| !func && parent instanceof AST_Call && parent.expression === node) {
|
|| !immutable && parent instanceof AST_Call && parent.expression === node) {
|
||||||
return true;
|
return true;
|
||||||
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
|
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
|
||||||
return !func && is_modified(parent, level + 1);
|
return !immutable && is_modified(parent, level + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2167,7 +2195,7 @@ merge(Compressor.prototype, {
|
|||||||
if (this.expression instanceof AST_Function
|
if (this.expression instanceof AST_Function
|
||||||
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
||||||
var node = this.clone();
|
var node = this.clone();
|
||||||
node.expression = node.expression.process_expression(false, compressor);
|
node.expression.process_expression(false, compressor);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -1996,6 +1996,146 @@ catch_var: {
|
|||||||
expect_stdout: "true"
|
expect_stdout: "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var_assign_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
var a;
|
||||||
|
a = 2;
|
||||||
|
console.log(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
console.log(2);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
var_assign_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
var a;
|
||||||
|
if (a = 2) console.log(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
if (2) console.log(2);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
var_assign_3: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
var a;
|
||||||
|
while (a = 2);
|
||||||
|
console.log(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
var a;
|
||||||
|
while (a = 2);
|
||||||
|
console.log(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var_assign_4: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function a() {
|
||||||
|
a = 2;
|
||||||
|
console.log(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function a() {
|
||||||
|
a = 2,
|
||||||
|
console.log(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var_assign_5: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
var a;
|
||||||
|
!function(b) {
|
||||||
|
a = 2;
|
||||||
|
console.log(a, b);
|
||||||
|
}(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
var a;
|
||||||
|
!function(b) {
|
||||||
|
a = 2,
|
||||||
|
console.log(a, b);
|
||||||
|
}(a);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "2 undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
immutable: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function() {
|
||||||
|
var a = "test";
|
||||||
|
console.log(a.indexOf("e"));
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
console.log("test".indexOf("e"));
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
}
|
||||||
|
|
||||||
issue_1814_1: {
|
issue_1814_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user