aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ast.js12
-rw-r--r--lib/compress.js74
-rw-r--r--test/compress/reduce_vars.js140
3 files changed, 193 insertions, 33 deletions
diff --git a/lib/ast.js b/lib/ast.js
index 0fa051b8..739c21c2 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -182,21 +182,13 @@ var AST_BlockStatement = DEFNODE("BlockStatement", null, {
}, AST_Block);
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
- $documentation: "The empty statement (empty block or simply a semicolon)",
- _walk: function(visitor) {
- return visitor._visit(this);
- }
+ $documentation: "The empty statement (empty block or simply a semicolon)"
}, AST_Statement);
var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", {
$documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`",
$propdoc: {
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);
@@ -551,11 +543,11 @@ var AST_Call = DEFNODE("Call", "expression args", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
- this.expression._walk(visitor);
var args = this.args;
for (var i = 0, len = args.length; i < len; i++) {
args[i]._walk(visitor);
}
+ this.expression._walk(visitor);
});
}
});
diff --git a/lib/compress.js b/lib/compress.js
index 6062be54..640fcecc 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -119,7 +119,7 @@ merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
compress: function(node) {
if (this.option("expression")) {
- node = node.process_expression(true);
+ node.process_expression(true);
}
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
@@ -128,7 +128,7 @@ merge(Compressor.prototype, {
node = node.transform(this);
}
if (this.option("expression")) {
- node = node.process_expression(false);
+ node.process_expression(false);
}
return node;
},
@@ -200,7 +200,7 @@ merge(Compressor.prototype, {
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 tt = new TreeTransformer(function(node) {
if (insert && node instanceof AST_SimpleStatement) {
@@ -244,10 +244,10 @@ merge(Compressor.prototype, {
}
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 toplevel = compressor.option("toplevel");
var safe_ids = Object.create(null);
@@ -258,7 +258,7 @@ merge(Compressor.prototype, {
d.fixed = false;
}
});
- var tw = new TreeWalker(function(node, descend){
+ var tw = new TreeWalker(function(node, descend) {
node._squeezed = false;
node._optimized = false;
if (reduce_vars) {
@@ -268,7 +268,7 @@ merge(Compressor.prototype, {
var d = node.definition();
d.references.push(node);
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;
}
}
@@ -293,6 +293,20 @@ merge(Compressor.prototype, {
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) {
var d = node.name.definition();
if (!toplevel && d.global || is_safe(d)) {
@@ -309,21 +323,24 @@ merge(Compressor.prototype, {
}
var iife;
if (node instanceof AST_Function
- && !node.name
&& (iife = tw.parent()) instanceof AST_Call
&& iife.expression === node) {
- // Virtually turn IIFE parameters into variable definitions:
- // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
- // So existing transformation rules can work on them.
- node.argnames.forEach(function(arg, i) {
- var d = arg.definition();
- d.fixed = function() {
- return iife.args[i] || make_node(AST_Undefined, iife);
- };
- mark(d, true);
- });
+ if (node.name) {
+ node.name.definition().fixed = node;
+ } else {
+ // Virtually turn IIFE parameters into variable definitions:
+ // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
+ // So existing transformation rules can work on them.
+ node.argnames.forEach(function(arg, i) {
+ var d = arg.definition();
+ 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);
push();
node.body.walk(tw);
@@ -335,6 +352,13 @@ merge(Compressor.prototype, {
}
return true;
}
+ if (node instanceof AST_DWLoop) {
+ push();
+ node.condition.walk(tw);
+ node.body.walk(tw);
+ pop();
+ return true;
+ }
if (node instanceof AST_LabeledStatement) {
push();
node.body.walk(tw);
@@ -401,13 +425,17 @@ merge(Compressor.prototype, {
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);
if (is_lhs(node, parent)
- || !func && parent instanceof AST_Call && parent.expression === node) {
+ || !immutable && parent instanceof AST_Call && parent.expression === node) {
return true;
} 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
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
var node = this.clone();
- node.expression = node.expression.process_expression(false, compressor);
+ node.expression.process_expression(false, compressor);
return node;
}
return this;
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 7621dd4a..6e079c1a 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -1996,6 +1996,146 @@ catch_var: {
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: {
options = {
evaluate: true,