[Pkg-javascript-commits] [uglifyjs] 57/228: fix corner cases in `reduce_vars` (#1524)

Jonas Smedegaard dr at jones.dk
Sat Apr 15 14:25:16 UTC 2017


This is an automated email from the git hooks/post-receive script.

js pushed a commit to branch master
in repository uglifyjs.

commit 7aa69117e1a38e4aeead13100ac952ca99dbb07f
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Thu Mar 2 00:20:53 2017 +0800

    fix corner cases in `reduce_vars` (#1524)
    
    Avoid variable substitution in the following cases:
    - use of variable before declaration
    - declaration within conditional code blocks
    - declaration within loop body
    
    fixes #1518
    fixes #1525
---
 lib/compress.js              | 107 ++++++++++++++++++++++++++++++--------
 test/compress/reduce_vars.js | 119 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 206 insertions(+), 20 deletions(-)

diff --git a/lib/compress.js b/lib/compress.js
index 32e7a64..2455097 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -179,38 +179,105 @@ merge(Compressor.prototype, {
 
     AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
         var reduce_vars = rescan && compressor.option("reduce_vars");
-        var unsafe = compressor.option("unsafe");
+        var safe_ids = [];
+        push();
         var tw = new TreeWalker(function(node){
+            if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
+                node._squeezed = false;
+                node._optimized = false;
+            }
             if (reduce_vars) {
                 if (node instanceof AST_Toplevel) node.globals.each(reset_def);
                 if (node instanceof AST_Scope) node.variables.each(reset_def);
                 if (node instanceof AST_SymbolRef) {
                     var d = node.definition();
                     d.references.push(node);
-                    if (d.fixed && (d.orig.length > 1 || isModified(node, 0))) {
+                    if (!d.fixed || isModified(node, 0) || !is_safe(d)) {
+                        d.fixed = false;
+                    }
+                }
+                if (node instanceof AST_VarDef) {
+                    var d = node.name.definition();
+                    if (d.fixed === undefined) {
+                        d.fixed = node.value || make_node(AST_Undefined, node);
+                        mark_as_safe(d);
+                    } else {
                         d.fixed = false;
                     }
                 }
-                if (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);
+                var iife;
+                if (node instanceof AST_Function
+                    && (iife = tw.parent()) instanceof AST_Call
+                    && iife.expression === node) {
+                    node.argnames.forEach(function(arg, i) {
+                        var d = arg.definition();
+                        d.fixed = iife.args[i] || make_node(AST_Undefined, iife);
+                        mark_as_safe(d);
                     });
                 }
-            }
-            if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
-                node._squeezed = false;
-                node._optimized = false;
+                if (node instanceof AST_If || node instanceof AST_DWLoop) {
+                    node.condition.walk(tw);
+                    push();
+                    node.body.walk(tw);
+                    pop();
+                    if (node.alternative) {
+                        push();
+                        node.alternative.walk(tw);
+                        pop();
+                    }
+                    return true;
+                }
+                if (node instanceof AST_LabeledStatement) {
+                    push();
+                    node.body.walk(tw);
+                    pop();
+                    return true;
+                }
+                if (node instanceof AST_For) {
+                    if (node.init) node.init.walk(tw);
+                    push();
+                    if (node.condition) node.condition.walk(tw);
+                    node.body.walk(tw);
+                    if (node.step) node.step.walk(tw);
+                    pop();
+                    return true;
+                }
+                if (node instanceof AST_ForIn) {
+                    if (node.init instanceof AST_SymbolRef) {
+                        node.init.definition().fixed = false;
+                    }
+                    node.object.walk(tw);
+                    push();
+                    node.body.walk(tw);
+                    pop();
+                    return true;
+                }
             }
         });
         this.walk(tw);
 
+        function mark_as_safe(def) {
+            safe_ids[safe_ids.length - 1][def.id] = true;
+        }
+
+        function is_safe(def) {
+            for (var i = safe_ids.length, id = def.id; --i >= 0;) {
+                if (safe_ids[i][id]) return true;
+            }
+        }
+
+        function push() {
+            safe_ids.push(Object.create(null));
+        }
+
+        function pop() {
+            safe_ids.pop();
+        }
+
         function reset_def(def) {
-            def.fixed = true;
+            def.fixed = undefined;
             def.references = [];
             def.should_replace = undefined;
-            if (unsafe && def.init) {
-                def.init._evaluated = undefined;
-            }
         }
 
         function isModified(node, level) {
@@ -1263,14 +1330,14 @@ merge(Compressor.prototype, {
             this._evaluating = true;
             try {
                 var d = this.definition();
-                if (compressor.option("reduce_vars") && d.fixed && d.init) {
+                if (compressor.option("reduce_vars") && d.fixed) {
                     if (compressor.option("unsafe")) {
-                        if (d.init._evaluated === undefined) {
-                            d.init._evaluated = ev(d.init, compressor);
+                        if (!HOP(d.fixed, "_evaluated")) {
+                            d.fixed._evaluated = ev(d.fixed, compressor);
                         }
-                        return d.init._evaluated;
+                        return d.fixed._evaluated;
                     }
-                    return ev(d.init, compressor);
+                    return ev(d.fixed, compressor);
                 }
             } finally {
                 this._evaluating = false;
@@ -3046,9 +3113,9 @@ merge(Compressor.prototype, {
         }
         if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
             var d = self.definition();
-            if (d.fixed && d.init) {
+            if (d.fixed) {
                 if (d.should_replace === undefined) {
-                    var init = d.init.evaluate(compressor);
+                    var init = d.fixed.evaluate(compressor);
                     if (init.length > 1) {
                         var value = init[0].print_to_string().length;
                         var name = d.name.length;
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 0ee201c..e38c317 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -470,3 +470,122 @@ multi_def_2: {
         var repeatLength = this.getBits(bitsLength) + bitsOffset;
     }
 }
+
+use_before_var: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        console.log(t);
+        var t = 1;
+    }
+    expect: {
+        console.log(t);
+        var t = 1;
+    }
+}
+
+inner_var_if: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        function f(){
+            return 0;
+        }
+        if (f())
+            var t = 1;
+        if (!t)
+            console.log(t);
+    }
+    expect: {
+        function f(){
+            return 0;
+        }
+        if (f())
+            var t = 1;
+        if (!t)
+            console.log(t);
+    }
+}
+
+inner_var_label: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        function f(){
+            return 1;
+        }
+        l: {
+            if (f()) break l;
+            var t = 1;
+        }
+        console.log(t);
+    }
+    expect: {
+        function f(){
+            return 1;
+        }
+        l: {
+            if (f()) break l;
+            var t = 1;
+        }
+        console.log(t);
+    }
+}
+
+inner_var_for: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        var a = 1;
+        x(a, b, d);
+        for (var b = 2, c = 3; x(a, b, c, d); x(a, b, c, d)) {
+            var d = 4, e = 5;
+            x(a, b, c, d, e);
+        }
+        x(a, b, c, d, e)
+    }
+    expect: {
+        var a = 1;
+        x(1, b, d);
+        for (var b = 2, c = 3; x(1, b, 3, d); x(1, b, 3, d)) {
+            var d = 4, e = 5;
+            x(1, b, 3, d, e);
+        }
+        x(1, b, 3, d, e);
+    }
+}
+
+inner_var_for_in: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        var a = 1, b = 2;
+        for (b in (function() {
+            return x(a, b, c);
+        })()) {
+            var c = 3, d = 4;
+            x(a, b, c, d);
+        }
+        x(a, b, c, d);
+    }
+    expect: {
+        var a = 1, b = 2;
+        for (b in (function() {
+            return x(1, b, c);
+        })()) {
+            var c = 3, d = 4;
+            x(1, b, c, d);
+        }
+        x(1, b, c, d);
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/uglifyjs.git



More information about the Pkg-javascript-commits mailing list