aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralexlamsl <alexlamsl@gmail.com>2017-02-18 19:19:55 +0800
committeralexlamsl <alexlamsl@gmail.com>2017-02-21 13:29:58 +0800
commita0f4fd390a0a1af80964aab9754bf5358db575e2 (patch)
treeb07e7f034909cb2b234fb8963575b6e0f8ff2ef5
parentb8b133d91a7a65f3375d391a036623901d1e357f (diff)
downloadtracifyjs-a0f4fd390a0a1af80964aab9754bf5358db575e2.tar.gz
tracifyjs-a0f4fd390a0a1af80964aab9754bf5358db575e2.zip
improve reduce_vars and fix a bug
- update modified flag between compress() passes - support IIFE arguments - fix corner case with multiple definitions closes #1473
-rw-r--r--lib/compress.js41
-rw-r--r--lib/scope.js26
-rw-r--r--test/compress/reduce_vars.js124
3 files changed, 161 insertions, 30 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 345a414a..72afe92a 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -108,7 +108,8 @@ merge(Compressor.prototype, {
compress: function(node) {
var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) {
- if (pass > 0) node.clear_opt_flags();
+ if (pass > 0 || this.option("reduce_vars"))
+ node.reset_opt_flags(this);
node = node.transform(this);
}
return node;
@@ -167,19 +168,45 @@ merge(Compressor.prototype, {
return this.print_to_string() == node.print_to_string();
});
- AST_Node.DEFMETHOD("clear_opt_flags", function(){
- this.walk(new TreeWalker(function(node){
+ AST_Node.DEFMETHOD("reset_opt_flags", function(compressor){
+ var reduce_vars = compressor.option("reduce_vars");
+ var tw = new TreeWalker(function(node){
+ if (reduce_vars && node instanceof AST_Scope) {
+ node.variables.each(function(def) {
+ delete def.modified;
+ });
+ }
if (node instanceof AST_SymbolRef) {
var d = node.definition();
- if (d && d.init) {
+ if (d.init) {
delete d.init._evaluated;
}
+ if (reduce_vars && (d.orig.length > 1 || isModified(node, 0))) {
+ d.modified = true;
+ }
+ }
+ if (reduce_vars && node instanceof AST_Call && node.expression instanceof AST_Function) {
+ node.expression.argnames.forEach(function(arg, i) {
+ arg.definition().init = node.args[i] || make_node(AST_Undefined, node);
+ });
}
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
node._squeezed = false;
node._optimized = false;
}
- }));
+ });
+ this.walk(tw);
+
+ function isModified(node, level) {
+ var parent = tw.parent(level);
+ if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--")
+ || parent instanceof AST_Assign && parent.left === node
+ || parent instanceof AST_Call && parent.expression === node) {
+ return true;
+ } else if (parent instanceof AST_PropAccess && parent.expression === node) {
+ return isModified(parent, level + 1);
+ }
+ }
});
function make_node(ctor, orig, props) {
@@ -459,7 +486,7 @@ merge(Compressor.prototype, {
var_defs_removed = true;
}
// Further optimize statement after substitution.
- stat.clear_opt_flags();
+ stat.reset_opt_flags(compressor);
compressor.warn("Replacing " + (is_constant ? "constant" : "variable") +
" " + var_name + " [{file}:{line},{col}]", node.start);
@@ -1158,7 +1185,7 @@ merge(Compressor.prototype, {
this._evaluating = true;
try {
var d = this.definition();
- if (d && (d.constant || compressor.option("reduce_vars") && !d.modified) && d.init) {
+ if ((d.constant || compressor.option("reduce_vars") && !d.modified) && d.init) {
if (compressor.option("unsafe")) {
if (!HOP(d.init, '_evaluated')) {
d.init._evaluated = ev(d.init, compressor);
diff --git a/lib/scope.js b/lib/scope.js
index d5cadd34..6ad12616 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -183,17 +183,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var func = null;
var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){
- function isModified(node, level) {
- var parent = tw.parent(level);
- if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--")
- || parent instanceof AST_Assign && parent.left === node
- || parent instanceof AST_Call && parent.expression === node) {
- return true;
- } else if (parent instanceof AST_PropAccess && parent.expression === node) {
- return isModified(parent, level + 1);
- }
- }
-
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
@@ -217,21 +206,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.scope.uses_arguments = true;
}
if (!sym) {
- var g;
if (globals.has(name)) {
- g = globals.get(name);
+ sym = globals.get(name);
} else {
- g = new SymbolDef(self, globals.size(), node);
- g.undeclared = true;
- g.global = true;
- globals.set(name, g);
+ sym = new SymbolDef(self, globals.size(), node);
+ sym.undeclared = true;
+ sym.global = true;
+ globals.set(name, sym);
}
- sym = g;
}
node.thedef = sym;
- if (isModified(node, 0)) {
- sym.modified = true;
- }
node.reference(options);
return true;
}
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 2301a92a..d9d02efa 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -108,8 +108,6 @@ modified: {
}
console.log(a + b);
console.log(b + c);
- // TODO: as "modified" is determined in "figure_out_scope",
- // even "passes" wouldn't improve this any further
console.log(a + c);
console.log(a + b + c);
}
@@ -350,3 +348,125 @@ unsafe_evaluate_equality: {
}
}
}
+
+passes: {
+ options = {
+ conditionals: true,
+ evaluate: true,
+ passes: 2,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ function f() {
+ var a = 1, b = 2, c = 3;
+ if (a) {
+ b = c;
+ } else {
+ c = b;
+ }
+ console.log(a + b);
+ console.log(b + c);
+ console.log(a + c);
+ console.log(a + b + c);
+ }
+ }
+ expect: {
+ function f() {
+ var b = 2, c = 3;
+ b = c;
+ console.log(1 + b);
+ console.log(b + 3);
+ console.log(4);
+ console.log(1 + b + 3);
+ }
+ }
+}
+
+iife: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ !function(a, b, c) {
+ b++;
+ console.log(a - 1, b * 1, c + 2);
+ }(1, 2, 3);
+ }
+ expect: {
+ !function(a, b, c) {
+ b++;
+ console.log(0, 1 * b, 5);
+ }(1, 2, 3);
+ }
+}
+
+iife_new: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ var A = new function(a, b, c) {
+ b++;
+ console.log(a - 1, b * 1, c + 2);
+ }(1, 2, 3);
+ }
+ expect: {
+ var A = new function(a, b, c) {
+ b++;
+ console.log(0, 1 * b, 5);
+ }(1, 2, 3);
+ }
+}
+
+multi_def: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ function f(a) {
+ if (a)
+ var b = 1;
+ else
+ var b = 2
+ console.log(b + 1);
+ }
+ }
+ expect: {
+ function f(a) {
+ if (a)
+ var b = 1;
+ else
+ var b = 2
+ console.log(b + 1);
+ }
+ }
+}
+
+multi_def_2: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ }
+ input: {
+ if (code == 16)
+ var bitsLength = 2, bitsOffset = 3, what = len;
+ else if (code == 17)
+ var bitsLength = 3, bitsOffset = 3, what = (len = 0);
+ else if (code == 18)
+ var bitsLength = 7, bitsOffset = 11, what = (len = 0);
+ var repeatLength = this.getBits(bitsLength) + bitsOffset;
+ }
+ expect: {
+ if (16 == code)
+ var bitsLength = 2, bitsOffset = 3, what = len;
+ else if (17 == code)
+ var bitsLength = 3, bitsOffset = 3, what = (len = 0);
+ else if (18 == code)
+ var bitsLength = 7, bitsOffset = 11, what = (len = 0);
+ var repeatLength = this.getBits(bitsLength) + bitsOffset;
+ }
+}