[Pkg-javascript-commits] [uglifyjs] 06/228: augment evaluate to extract within objects (#1425)

Jonas Smedegaard dr at jones.dk
Sat Apr 15 14:25:11 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 0d7d4918eb6fb73c3cf9863479b3e66d38fad6df
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Thu Jan 26 19:14:18 2017 +0800

    augment evaluate to extract within objects (#1425)
    
    - gated by `unsafe`
    - replaces previous optimisation specific to String.length
    - "123"[0] => 1
    - [1, 2, 3][0] => 1
    - [1, 2, 3].length => 3
    - does not apply to objects with overridden prototype functions
---
 lib/compress.js              |  66 +++++++-
 lib/scope.js                 |  17 +-
 test/compress/arrays.js      |  49 ++++++
 test/compress/evaluate.js    | 377 +++++++++++++++++++++++++++++++++++++++++++
 test/compress/properties.js  |  51 +++++-
 test/compress/reduce_vars.js | 183 ++++++++++++++++++++-
 6 files changed, 729 insertions(+), 14 deletions(-)

diff --git a/lib/compress.js b/lib/compress.js
index bbd3659..5c01962 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -152,6 +152,12 @@ merge(Compressor.prototype, {
 
     AST_Node.DEFMETHOD("clear_opt_flags", function(){
         this.walk(new TreeWalker(function(node){
+            if (node instanceof AST_SymbolRef) {
+                var d = node.definition();
+                if (d && d.init) {
+                    delete d.init._evaluated;
+                }
+            }
             if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
                 node._squeezed = false;
                 node._optimized = false;
@@ -992,13 +998,20 @@ merge(Compressor.prototype, {
         // constant; otherwise it's the original or a replacement node.
         AST_Node.DEFMETHOD("evaluate", function(compressor){
             if (!compressor.option("evaluate")) return [ this ];
+            var val;
             try {
-                var val = this._eval(compressor);
-                return [ best_of(make_node_from_constant(compressor, val, this), this), val ];
+                val = this._eval(compressor);
             } catch(ex) {
                 if (ex !== def) throw ex;
                 return [ this ];
             }
+            var node;
+            try {
+                node = make_node_from_constant(compressor, val, this);
+            } catch(ex) {
+                return [ this ];
+            }
+            return [ best_of(node, this), val ];
         });
         AST_Node.DEFMETHOD("is_constant", function(compressor){
             // Accomodate when compress option evaluate=false
@@ -1047,6 +1060,32 @@ merge(Compressor.prototype, {
         def(AST_Constant, function(){
             return this.getValue();
         });
+        def(AST_Array, function(compressor){
+            if (compressor.option("unsafe")) {
+                return this.elements.map(function(element) {
+                    return ev(element, compressor);
+                });
+            }
+            throw def;
+        });
+        def(AST_Object, function(compressor){
+            if (compressor.option("unsafe")) {
+                var val = {};
+                for (var i = 0, len = this.properties.length; i < len; i++) {
+                    var prop = this.properties[i];
+                    var key = prop.key;
+                    if (key instanceof AST_Node) {
+                        key = ev(key, compressor);
+                    }
+                    if (typeof Object.prototype[key] === 'function') {
+                        throw def;
+                    }
+                    val[key] = ev(prop.value, compressor);
+                }
+                return val;
+            }
+            throw def;
+        });
         def(AST_UnaryPrefix, function(compressor){
             var e = this.expression;
             switch (this.operator) {
@@ -1114,6 +1153,12 @@ merge(Compressor.prototype, {
             try {
                 var d = this.definition();
                 if (d && (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);
+                        }
+                        return d.init._evaluated;
+                    }
                     return ev(d.init, compressor);
                 }
             } finally {
@@ -1121,11 +1166,16 @@ merge(Compressor.prototype, {
             }
             throw def;
         });
-        def(AST_Dot, function(compressor){
-            if (compressor.option("unsafe") && this.property == "length") {
-                var str = ev(this.expression, compressor);
-                if (typeof str == "string")
-                    return str.length;
+        def(AST_PropAccess, function(compressor){
+            if (compressor.option("unsafe")) {
+                var key = this.property;
+                if (key instanceof AST_Node) {
+                    key = ev(key, compressor);
+                }
+                var val = ev(this.expression, compressor);
+                if (val && HOP(val, key)) {
+                    return val[key];
+                }
             }
             throw def;
         });
@@ -2890,7 +2940,7 @@ merge(Compressor.prototype, {
                 });
             }
         }
-        return self;
+        return self.evaluate(compressor)[0];
     });
 
     OPT(AST_Dot, function(self, compressor){
diff --git a/lib/scope.js b/lib/scope.js
index bc5db90..ae792a0 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -184,6 +184,17 @@ 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;
@@ -197,8 +208,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
         }
         if (node instanceof AST_SymbolRef) {
             var name = node.name;
-            var parent = tw.parent();
-            if (name == "eval" && parent instanceof AST_Call) {
+            if (name == "eval" && tw.parent() instanceof AST_Call) {
                 for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
                     s.uses_eval = true;
                 }
@@ -220,8 +230,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
                 sym = g;
             }
             node.thedef = sym;
-            if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--")
-             || parent instanceof AST_Assign && parent.left === node) {
+            if (isModified(node, 0)) {
                 sym.modified = true;
             }
             node.reference();
diff --git a/test/compress/arrays.js b/test/compress/arrays.js
index e636347..807ba20 100644
--- a/test/compress/arrays.js
+++ b/test/compress/arrays.js
@@ -72,3 +72,52 @@ constant_join_2: {
         var f = "strstr" + variable + "foobarmoo" + foo;
     }
 }
+
+for_loop: {
+    options = {
+        unsafe      : true,
+        evaluate    : true,
+        reduce_vars : true
+    };
+    input: {
+        function f0() {
+            var a = [1, 2, 3];
+            for (var i = 0; i < a.length; i++) {
+                console.log(a[i]);
+            }
+        }
+
+        function f1() {
+            var a = [1, 2, 3];
+            for (var i = 0, len = a.length; i < len; i++) {
+                console.log(a[i]);
+            }
+        }
+
+        function f2() {
+            var a = [1, 2, 3];
+            for (var i = 0; i < a.length; i++) {
+                a[i]++;
+            }
+        }
+    }
+    expect: {
+        function f0() {
+            var a = [1, 2, 3];
+            for (var i = 0; i < 3; i++)
+                console.log(a[i]);
+        }
+
+        function f1() {
+            var a = [1, 2, 3];
+            for (var i = 0, len = 3; i < len; i++)
+                console.log(a[i]);
+        }
+
+        function f2() {
+            var a = [1, 2, 3];
+            for (var i = 0; i < a.length; i++)
+                a[i]++;
+        }
+    }
+}
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index d27582f..c74c7b2 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -37,3 +37,380 @@ positive_zero: {
         );
     }
 }
+
+unsafe_constant: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            true.a,
+            false.a,
+            null.a,
+            undefined.a
+        );
+    }
+    expect: {
+        console.log(
+            true.a,
+            false.a,
+            null.a,
+            (void 0).a
+        );
+    }
+}
+
+unsafe_object: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({a:1}) + 1,
+            ({a:1}).a + 1,
+            ({a:1}).b + 1,
+            ({a:1}).a.b + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({a:1}) + 1,
+            2,
+            ({a:1}).b + 1,
+            1..b + 1
+        );
+    }
+}
+
+unsafe_object_nested: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({a:{b:1}}) + 1,
+            ({a:{b:1}}).a + 1,
+            ({a:{b:1}}).b + 1,
+            ({a:{b:1}}).a.b + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({a:{b:1}}) + 1,
+            ({a:{b:1}}).a + 1,
+            ({a:{b:1}}).b + 1,
+            2
+        );
+    }
+}
+
+unsafe_object_complex: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({a:{b:1},b:1}) + 1,
+            ({a:{b:1},b:1}).a + 1,
+            ({a:{b:1},b:1}).b + 1,
+            ({a:{b:1},b:1}).a.b + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({a:{b:1},b:1}) + 1,
+            ({a:{b:1},b:1}).a + 1,
+            2,
+            2
+        );
+    }
+}
+
+unsafe_object_repeated: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({a:{b:1},a:1}) + 1,
+            ({a:{b:1},a:1}).a + 1,
+            ({a:{b:1},a:1}).b + 1,
+            ({a:{b:1},a:1}).a.b + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({a:{b:1},a:1}) + 1,
+            2,
+            ({a:{b:1},a:1}).b + 1,
+            1..b + 1
+        );
+    }
+}
+
+unsafe_function: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({a:{b:1},b:function(){}}) + 1,
+            ({a:{b:1},b:function(){}}).a + 1,
+            ({a:{b:1},b:function(){}}).b + 1,
+            ({a:{b:1},b:function(){}}).a.b + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({a:{b:1},b:function(){}}) + 1,
+            ({a:{b:1},b:function(){}}).a + 1,
+            ({a:{b:1},b:function(){}}).b + 1,
+            ({a:{b:1},b:function(){}}).a.b + 1
+        );
+    }
+}
+
+unsafe_integer_key: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({0:1}) + 1,
+            ({0:1})[0] + 1,
+            ({0:1})["0"] + 1,
+            ({0:1})[1] + 1,
+            ({0:1})[0][1] + 1,
+            ({0:1})[0]["1"] + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({0:1}) + 1,
+            2,
+            2,
+            ({0:1})[1] + 1,
+            1[1] + 1,
+            1["1"] + 1
+        );
+    }
+}
+
+unsafe_integer_key_complex: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({0:{1:1},1:1}) + 1,
+            ({0:{1:1},1:1})[0] + 1,
+            ({0:{1:1},1:1})["0"] + 1,
+            ({0:{1:1},1:1})[1] + 1,
+            ({0:{1:1},1:1})[0][1] + 1,
+            ({0:{1:1},1:1})[0]["1"] + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({0:{1:1},1:1}) + 1,
+            "[object Object]1",
+            "[object Object]1",
+            2,
+            2,
+            2
+        );
+    }
+}
+
+unsafe_float_key: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({2.72:1}) + 1,
+            ({2.72:1})[2.72] + 1,
+            ({2.72:1})["2.72"] + 1,
+            ({2.72:1})[3.14] + 1,
+            ({2.72:1})[2.72][3.14] + 1,
+            ({2.72:1})[2.72]["3.14"] + 1
+        );
+    }
+    expect: {
+        console.log(
+            ({2.72:1}) + 1,
+            2,
+            2,
+            ({2.72:1})[3.14] + 1,
+            1[3.14] + 1,
+            1["3.14"] + 1
+        );
+    }
+}
+
+unsafe_float_key_complex: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            ({2.72:{3.14:1},3.14:1}) + 1,
+            ({2.72:{3.14:1},3.14:1})[2.72] + 1,
+            ({2.72:{3.14:1},3.14:1})["2.72"] + 1,
+            ({2.72:{3.14:1},3.14:1})[3.14] + 1,
+            ({2.72:{3.14:1},3.14:1})[2.72][3.14] + 1,
+            ({2.72:{3.14:1},3.14:1})[2.72]["3.14"] + 1
+        );
+    }
+    expect: {
+        console.log(
+            "[object Object]1",
+            "[object Object]1",
+            "[object Object]1",
+            2,
+            2,
+            2
+        );
+    }
+}
+
+unsafe_array: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            [1, , 3][1],
+            [1, 2, 3, a] + 1,
+            [1, 2, 3, 4] + 1,
+            [1, 2, 3, a][0] + 1,
+            [1, 2, 3, 4][0] + 1,
+            [1, 2, 3, 4][6 - 5] + 1,
+            [1, , 3, 4][6 - 5] + 1,
+            [[1, 2], [3, 4]][0] + 1,
+            [[1, 2], [3, 4]][6 - 5][1] + 1,
+            [[1, 2], , [3, 4]][6 - 5][1] + 1
+        );
+    }
+    expect: {
+        console.log(
+            void 0,
+            [1, 2, 3, a] + 1,
+            "1,2,3,41",
+            [1, 2, 3, a][0] + 1,
+            2,
+            3,
+            NaN,
+            "1,21",
+            5,
+            (void 0)[1] + 1
+        );
+    }
+}
+
+unsafe_string: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            "1234" + 1,
+            "1234"[0] + 1,
+            "1234"[6 - 5] + 1,
+            ("12" + "34")[0] + 1,
+            ("12" + "34")[6 - 5] + 1,
+            [1, 2, 3, 4].join("")[0] + 1
+        );
+    }
+    expect: {
+        console.log(
+            "12341",
+            "11",
+            "21",
+            "11",
+            "21",
+            "11"
+        );
+    }
+}
+
+unsafe_array_bad_index: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            [1, 2, 3, 4].a + 1,
+            [1, 2, 3, 4]["a"] + 1,
+            [1, 2, 3, 4][3.14] + 1
+        );
+    }
+    expect: {
+        console.log(
+            [1, 2, 3, 4].a + 1,
+            [1, 2, 3, 4]["a"] + 1,
+            [1, 2, 3, 4][3.14] + 1
+        );
+    }
+}
+
+unsafe_string_bad_index: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        console.log(
+            "1234".a + 1,
+            "1234"["a"] + 1,
+            "1234"[3.14] + 1
+        );
+    }
+    expect: {
+        console.log(
+            "1234".a + 1,
+            "1234"["a"] + 1,
+            "1234"[3.14] + 1
+        );
+    }
+}
+
+unsafe_prototype_function: {
+    options = {
+        evaluate  : true,
+        unsafe    : true
+    }
+    input: {
+        var a = ({valueOf: 0}) < 1;
+        var b = ({toString: 0}) < 1;
+        var c = ({valueOf: 0}) + "";
+        var d = ({toString: 0}) + "";
+        var e = (({valueOf: 0}) + "")[2];
+        var f = (({toString: 0}) + "")[2];
+        var g = ({valueOf: 0}).valueOf();
+        var h = ({toString: 0}).toString();
+    }
+    expect: {
+        var a = ({valueOf: 0}) < 1;
+        var b = ({toString: 0}) < 1;
+        var c = ({valueOf: 0}) + "";
+        var d = ({toString: 0}) + "";
+        var e = (({valueOf: 0}) + "")[2];
+        var f = (({toString: 0}) + "")[2];
+        var g = ({valueOf: 0}).valueOf();
+        var h = "" + ({toString: 0});
+    }
+}
diff --git a/test/compress/properties.js b/test/compress/properties.js
index 22f2c33..7ad54eb 100644
--- a/test/compress/properties.js
+++ b/test/compress/properties.js
@@ -54,7 +54,56 @@ dot_properties_es5: {
     }
 }
 
-evaluate_length: {
+sub_properties: {
+    options = {
+        evaluate: true,
+        properties: true
+    };
+    input: {
+        a[0] = 0;
+        a["0"] = 1;
+        a[3.14] = 2;
+        a["3" + ".14"] = 3;
+        a["i" + "f"] = 4;
+        a["foo" + " bar"] = 5;
+        a[0 / 0] = 6;
+        a[null] = 7;
+        a[undefined] = 8;
+    }
+    expect: {
+        a[0] = 0;
+        a[0] = 1;
+        a[3.14] = 2;
+        a[3.14] = 3;
+        a.if = 4;
+        a["foo bar"] = 5;
+        a[NaN] = 6;
+        a[null] = 7;
+        a[void 0] = 8;
+    }
+}
+
+evaluate_array_length: {
+    options = {
+        properties: true,
+        unsafe: true,
+        evaluate: true
+    };
+    input: {
+        a = [1, 2, 3].length;
+        a = [1, 2, 3].join()["len" + "gth"];
+        a = [1, 2, b].length;
+        a = [1, 2, 3].join(b).length;
+    }
+    expect: {
+        a = 3;
+        a = 5;
+        a = [1, 2, b].length;
+        a = [1, 2, 3].join(b).length;
+    }
+}
+
+evaluate_string_length: {
     options = {
         properties: true,
         unsafe: true,
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index a1d0501..c401ac6 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -168,4 +168,185 @@ modified: {
             console.log(B ? 'yes' : 'no');
         }
    }
-}
\ No newline at end of file
+}
+
+unsafe_evaluate: {
+    options = {
+        evaluate     : true,
+        reduce_vars  : true,
+        unsafe       : true,
+        unused       : true
+    }
+    input: {
+        function f0(){
+            var a = {
+                b:1
+            };
+            console.log(a.b + 3);
+        }
+
+        function f1(){
+            var a = {
+                b:{
+                    c:1
+                },
+                d:2
+            };
+            console.log(a.b + 3, a.d + 4, a.b.c + 5, a.d.c + 6);
+        }
+    }
+    expect: {
+        function f0(){
+            console.log(4);
+        }
+
+        function f1(){
+            var a = {
+                b:{
+                    c:1
+                },
+                d:2
+            };
+            console.log(a.b + 3, 6, 6, 2..c + 6);
+        }
+    }
+}
+
+unsafe_evaluate_object: {
+    options = {
+        evaluate     : true,
+        reduce_vars  : true,
+        unsafe       : true
+    }
+    input: {
+        function f0(){
+            var a = 1;
+            var b = {};
+            b[a] = 2;
+            console.log(a + 3);
+        }
+
+        function f1(){
+            var a = {
+                b:1
+            };
+            a.b = 2;
+            console.log(a.b + 3);
+        }
+    }
+    expect: {
+        function f0(){
+            var a = 1;
+            var b = {};
+            b[a] = 2;
+            console.log(4);
+        }
+
+        function f1(){
+            var a = {
+                b:1
+            };
+            a.b = 2;
+            console.log(a.b + 3);
+        }
+    }
+}
+
+unsafe_evaluate_array: {
+    options = {
+        evaluate     : true,
+        reduce_vars  : true,
+        unsafe       : true
+    }
+    input: {
+        function f0(){
+            var a = 1;
+            var b = [];
+            b[a] = 2;
+            console.log(a + 3);
+        }
+
+        function f1(){
+            var a = [1];
+            a[2] = 3;
+            console.log(a.length);
+        }
+
+        function f2(){
+            var a = [1];
+            a.push(2);
+            console.log(a.length);
+        }
+    }
+    expect: {
+        function f0(){
+            var a = 1;
+            var b = [];
+            b[a] = 2;
+            console.log(4);
+        }
+
+        function f1(){
+            var a = [1];
+            a[2] = 3;
+            console.log(a.length);
+        }
+
+        function f2(){
+            var a = [1];
+            a.push(2);
+            console.log(a.length);
+        }
+    }
+}
+
+unsafe_evaluate_equality: {
+    options = {
+        evaluate     : true,
+        reduce_vars  : true,
+        unsafe       : true,
+        unused       : true
+    }
+    input: {
+        function f0(){
+            var a = {};
+            console.log(a === a);
+        }
+
+        function f1(){
+            var a = [];
+            console.log(a === a);
+        }
+
+        function f2(){
+            var a = {a:1, b:2};
+            var b = a;
+            var c = a;
+            console.log(b === c);
+        }
+
+        function f3(){
+            var a = [1, 2, 3];
+            var b = a;
+            var c = a;
+            console.log(b === c);
+        }
+    }
+    expect: {
+        function f0(){
+            console.log(true);
+        }
+
+        function f1(){
+            console.log(true);
+        }
+
+        function f2(){
+            console.log(true);
+        }
+
+        function f3(){
+            console.log(true);
+        }
+    }
+}

-- 
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