[Pkg-javascript-commits] [uglifyjs] 186/228: improve compression of undefined, NaN & Infinitiy (#1748)

Jonas Smedegaard dr at jones.dk
Sat Apr 15 14:25:28 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 257ddc3bdb37efdb48fc23371f5f523e2044afd8
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Sat Apr 1 03:02:14 2017 +0800

    improve compression of undefined, NaN & Infinitiy (#1748)
    
    - migrate transformation logic from `OutputStream` to `Compressor`
    - always turn `undefined` into `void 0` (unless `unsafe`)
    - always keep `NaN` except when avoiding local variable redefinition
    - introduce `keep_infinity` to suppress `1/0` transform, except when avoiding local variable redefinition
    
    supersedes #1723
    fixes #1730
---
 README.md                     |  3 ++
 lib/compress.js               | 75 ++++++++++++++++++++++++++++++++++---------
 lib/output.js                 | 28 +---------------
 test/compress/conditionals.js |  8 ++---
 test/compress/evaluate.js     |  8 ++---
 test/compress/issue-1105.js   | 68 +++++++++++++++++++++++++++++++++++++--
 test/compress/issue-597.js    | 32 +++++++++++++++++-
 test/compress/numbers.js      |  2 +-
 test/compress/properties.js   |  2 +-
 9 files changed, 170 insertions(+), 56 deletions(-)

diff --git a/README.md b/README.md
index 2399e23..d57a15c 100644
--- a/README.md
+++ b/README.md
@@ -443,6 +443,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
   integer argument larger than 1 to further reduce code size in some cases.
   Note: raising the number of passes will increase uglify compress time.
 
+- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
+  being compressed into `1/0`, which may cause performance issues on Chrome.
+
 ### The `unsafe` option
 
 It enables some transformations that *might* break code logic in certain
diff --git a/lib/compress.js b/lib/compress.js
index a2332a9..1d9cd40 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -66,6 +66,7 @@ function Compressor(options, false_by_default) {
         join_vars     : !false_by_default,
         keep_fargs    : true,
         keep_fnames   : false,
+        keep_infinity : false,
         loops         : !false_by_default,
         negate_iife   : !false_by_default,
         passes        : 1,
@@ -215,7 +216,12 @@ merge(Compressor.prototype, {
                     }) : make_node(AST_EmptyStatement, node);
                 }
                 return make_node(AST_SimpleStatement, node, {
-                    body: node.value || make_node(AST_Undefined, node)
+                    body: node.value || make_node(AST_UnaryPrefix, node, {
+                        operator: "void",
+                        expression: make_node(AST_Number, node, {
+                            value: 0
+                        })
+                    })
                 });
             }
             if (node instanceof AST_Lambda && node !== self) {
@@ -1123,8 +1129,12 @@ merge(Compressor.prototype, {
         }));
     };
 
-    function is_undefined(node) {
-        return node instanceof AST_Undefined || node.is_undefined;
+    function is_undefined(node, compressor) {
+        return node.is_undefined
+            || node instanceof AST_Undefined
+            || node instanceof AST_UnaryPrefix
+                && node.operator == "void"
+                && !node.expression.has_side_effects(compressor);
     }
 
     /* -----[ boolean/negation helpers ]----- */
@@ -1313,7 +1323,7 @@ merge(Compressor.prototype, {
                 return this;
             }
         });
-        var unaryPrefix = makePredicate("! ~ - +");
+        var unaryPrefix = makePredicate("! ~ - + void");
         AST_Node.DEFMETHOD("is_constant", function(){
             // Accomodate when compress option evaluate=false
             // as well as the common constant expressions !0 and -1
@@ -2971,7 +2981,7 @@ merge(Compressor.prototype, {
                 }
             }
         }
-        if (is_undefined(self.cdr)) {
+        if (is_undefined(self.cdr, compressor)) {
             return make_node(AST_UnaryPrefix, self, {
                 operator   : "void",
                 expression : self.car
@@ -3010,7 +3020,7 @@ merge(Compressor.prototype, {
                 self.expression = e;
                 return self;
             } else {
-                return make_node(AST_Undefined, self).transform(compressor);
+                return make_node(AST_Undefined, self).optimize(compressor);
             }
         }
         if (compressor.option("booleans") && compressor.in_boolean_context()) {
@@ -3034,6 +3044,9 @@ merge(Compressor.prototype, {
                 })).optimize(compressor);
             }
         }
+        if (self.operator == "-" && e instanceof AST_Infinity) {
+            e = e.transform(compressor);
+        }
         if (e instanceof AST_Binary
             && (self.operator == "+" || self.operator == "-")
             && (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
@@ -3043,8 +3056,7 @@ merge(Compressor.prototype, {
         }
         // avoids infinite recursion of numerals
         if (self.operator != "-"
-            || !(self.expression instanceof AST_Number
-                || self.expression instanceof AST_Infinity)) {
+            || !(e instanceof AST_Number || e instanceof AST_Infinity)) {
             var ev = self.evaluate(compressor);
             if (ev !== self) {
                 ev = make_node_from_constant(ev, self).optimize(compressor);
@@ -3087,8 +3099,8 @@ merge(Compressor.prototype, {
 
     OPT(AST_Binary, function(self, compressor){
         function reversible() {
-            return self.left instanceof AST_Constant
-                || self.right instanceof AST_Constant
+            return self.left.is_constant()
+                || self.right.is_constant()
                 || !self.left.has_side_effects(compressor)
                     && !self.right.has_side_effects(compressor);
         }
@@ -3101,8 +3113,8 @@ merge(Compressor.prototype, {
             }
         }
         if (commutativeOperators(self.operator)) {
-            if (self.right instanceof AST_Constant
-                && !(self.left instanceof AST_Constant)) {
+            if (self.right.is_constant()
+                && !self.left.is_constant()) {
                 // if right is a constant, whatever side effects the
                 // left side might have could not influence the
                 // result.  hence, force switch.
@@ -3464,9 +3476,9 @@ merge(Compressor.prototype, {
               case "undefined":
                 return make_node(AST_Undefined, self).optimize(compressor);
               case "NaN":
-                return make_node(AST_NaN, self);
+                return make_node(AST_NaN, self).optimize(compressor);
               case "Infinity":
-                return make_node(AST_Infinity, self);
+                return make_node(AST_Infinity, self).optimize(compressor);
             }
         }
         if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
@@ -3508,7 +3520,38 @@ merge(Compressor.prototype, {
                 return ref;
             }
         }
-        return self;
+        return make_node(AST_UnaryPrefix, self, {
+            operator: "void",
+            expression: make_node(AST_Number, self, {
+                value: 0
+            })
+        });
+    });
+
+    OPT(AST_Infinity, function(self, compressor){
+        var retain = compressor.option("keep_infinity")
+            && !compressor.find_parent(AST_Scope).find_variable("Infinity");
+        return retain ? self : make_node(AST_Binary, self, {
+            operator: "/",
+            left: make_node(AST_Number, self, {
+                value: 1
+            }),
+            right: make_node(AST_Number, self, {
+                value: 0
+            })
+        });
+    });
+
+    OPT(AST_NaN, function(self, compressor){
+        return compressor.find_parent(AST_Scope).find_variable("NaN") ? make_node(AST_Binary, self, {
+            operator: "/",
+            left: make_node(AST_Number, self, {
+                value: 0
+            }),
+            right: make_node(AST_Number, self, {
+                value: 0
+            })
+        }) : self;
     });
 
     var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
@@ -3809,7 +3852,7 @@ merge(Compressor.prototype, {
     OPT(AST_RegExp, literals_in_boolean_context);
 
     OPT(AST_Return, function(self, compressor){
-        if (self.value && is_undefined(self.value)) {
+        if (self.value && is_undefined(self.value, compressor)) {
             self.value = null;
         }
         return self;
diff --git a/lib/output.js b/lib/output.js
index d5c7304..d0adbdd 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -586,21 +586,12 @@ function OutputStream(options) {
         return first_in_statement(output);
     });
 
-    PARENS([ AST_Unary, AST_Undefined ], function(output){
+    PARENS(AST_Unary, function(output){
         var p = output.parent();
         return p instanceof AST_PropAccess && p.expression === this
             || p instanceof AST_Call && p.expression === this;
     });
 
-    PARENS([ AST_Infinity, AST_NaN ], function(output){
-        var p = output.parent();
-        return p instanceof AST_PropAccess && p.expression === this
-            || p instanceof AST_Call && p.expression === this
-            || p instanceof AST_Unary && p.operator != "+" && p.operator != "-"
-            || p instanceof AST_Binary && p.right === this
-                && (p.operator == "/" || p.operator == "%");
-    });
-
     PARENS(AST_Seq, function(output){
         var p = output.parent();
         return p instanceof AST_Call             // (foo, bar)() or foo(1, (2, 3), 4)
@@ -1258,24 +1249,7 @@ function OutputStream(options) {
         var def = self.definition();
         output.print_name(def ? def.mangled_name || def.name : self.name);
     });
-    DEFPRINT(AST_Undefined, function(self, output){
-        output.print("void 0");
-    });
     DEFPRINT(AST_Hole, noop);
-    DEFPRINT(AST_Infinity, function(self, output){
-        output.print("1");
-        output.space();
-        output.print("/");
-        output.space();
-        output.print("0");
-    });
-    DEFPRINT(AST_NaN, function(self, output){
-        output.print("0");
-        output.space();
-        output.print("/");
-        output.space();
-        output.print("0");
-    });
     DEFPRINT(AST_This, function(self, output){
         output.print("this");
     });
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
index 54d4264..e7ea2bb 100644
--- a/test/compress/conditionals.js
+++ b/test/compress/conditionals.js
@@ -840,8 +840,8 @@ equality_conditionals_false: {
         f(0, true, 0),
         f(1, 2, 3),
         f(1, null, 3),
-        f(0/0),
-        f(0/0, "foo");
+        f(NaN),
+        f(NaN, "foo");
     }
     expect_stdout: true
 }
@@ -888,8 +888,8 @@ equality_conditionals_true: {
         f(0, true, 0),
         f(1, 2, 3),
         f(1, null, 3),
-        f(0/0),
-        f(0/0, "foo");
+        f(NaN),
+        f(NaN, "foo");
     }
     expect_stdout: true
 }
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index 35b6b92..0d26e74 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -52,7 +52,7 @@ and: {
         a = 7;
 
         a = false;
-        a = 0/0;
+        a = NaN;
         a = 0;
         a = void 0;
         a = null;
@@ -67,7 +67,7 @@ and: {
         a = 6 << condition   && -4.5;
 
         a = condition        && false;
-        a = console.log("b") && 0/0;
+        a = console.log("b") && NaN;
         a = console.log("c") && 0;
         a = 2 * condition    && void 0;
         a = condition + 3    && null;
@@ -149,7 +149,7 @@ or: {
         a = 6 << condition   || -4.5;
 
         a = condition        || false;
-        a = console.log("b") || 0/0;
+        a = console.log("b") || NaN;
         a = console.log("c") || 0;
         a = 2 * condition    || void 0;
         a = condition + 3    || null;
@@ -533,7 +533,7 @@ unsafe_array: {
             [1, 2, 3, a][0] + 1,
             2,
             3,
-            0/0,
+            NaN,
             "1,21",
             5,
             (void 0)[1] + 1
diff --git a/test/compress/issue-1105.js b/test/compress/issue-1105.js
index f941216..ea95793 100644
--- a/test/compress/issue-1105.js
+++ b/test/compress/issue-1105.js
@@ -193,6 +193,7 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
         cascade:       true,
         side_effects:  true,
         sequences:     false,
+        keep_infinity: false,
     }
     input: {
         var f = console.log;
@@ -224,10 +225,73 @@ assorted_Infinity_NaN_undefined_in_with_scope: {
         };
         if (o) {
             f(void 0, void 0);
-            f(0/0, 0/0);
+            f(NaN, NaN);
             f(1/0, 1/0);
             f(-1/0, -1/0);
-            f(0/0, 0/0);
+            f(NaN, NaN);
+        }
+        with (o) {
+            f(undefined, void 0);
+            f(NaN, 0/0);
+            f(Infinity, 1/0);
+            f(-Infinity, -1/0);
+            f(9 + undefined, 9 + void 0);
+        }
+    }
+    expect_stdout: true
+}
+
+assorted_Infinity_NaN_undefined_in_with_scope_keep_infinity: {
+    options = {
+        unused:        true,
+        evaluate:      true,
+        dead_code:     true,
+        conditionals:  true,
+        comparisons:   true,
+        booleans:      true,
+        hoist_funs:    true,
+        keep_fargs:    true,
+        if_return:     true,
+        join_vars:     true,
+        cascade:       true,
+        side_effects:  true,
+        sequences:     false,
+        keep_infinity: true,
+    }
+    input: {
+        var f = console.log;
+        var o = {
+            undefined : 3,
+            NaN       : 4,
+            Infinity  : 5,
+        };
+        if (o) {
+            f(undefined, void 0);
+            f(NaN, 0/0);
+            f(Infinity, 1/0);
+            f(-Infinity, -(1/0));
+            f(2 + 7 + undefined, 2 + 7 + void 0);
+        }
+        with (o) {
+            f(undefined, void 0);
+            f(NaN, 0/0);
+            f(Infinity, 1/0);
+            f(-Infinity, -(1/0));
+            f(2 + 7 + undefined, 2 + 7 + void 0);
+        }
+    }
+    expect: {
+        var f = console.log, o = {
+            undefined : 3,
+            NaN       : 4,
+            Infinity  : 5
+        };
+        if (o) {
+            f(void 0, void 0);
+            f(NaN, NaN);
+            f(Infinity, 1/0);
+            f(-Infinity, -1/0);
+            f(NaN, NaN);
         }
         with (o) {
             f(undefined, void 0);
diff --git a/test/compress/issue-597.js b/test/compress/issue-597.js
index 987bcac..143fcc2 100644
--- a/test/compress/issue-597.js
+++ b/test/compress/issue-597.js
@@ -6,7 +6,7 @@ NaN_and_Infinity_must_have_parens: {
     }
     expect: {
         (1/0).toString();
-        (0/0).toString();
+        NaN.toString();
     }
 }
 
@@ -24,6 +24,36 @@ NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: {
     }
 }
 
+NaN_and_Infinity_must_have_parens_evaluate: {
+    options = {
+        evaluate: true,
+    }
+    input: {
+        (123456789 / 0).toString();
+        (+"foo").toString();
+    }
+    expect: {
+        (1/0).toString();
+        NaN.toString();
+    }
+}
+
+NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined_evaluate: {
+    options = {
+        evaluate: true,
+    }
+    input: {
+        var Infinity, NaN;
+        (123456789 / 0).toString();
+        (+"foo").toString();
+    }
+    expect: {
+        var Infinity, NaN;
+        (1/0).toString();
+        (0/0).toString();
+    }
+}
+
 beautify_off_1: {
     options = {
         evaluate: true,
diff --git a/test/compress/numbers.js b/test/compress/numbers.js
index 946a7f2..86545fb 100644
--- a/test/compress/numbers.js
+++ b/test/compress/numbers.js
@@ -186,7 +186,7 @@ unary_binary_parenthesis: {
         });
     }
     expect: {
-        var v = [ 0, 1, 0/0, 1/0, null, void 0, true, false, "", "foo", /foo/ ];
+        var v = [ 0, 1, NaN, 1/0, null, void 0, true, false, "", "foo", /foo/ ];
         v.forEach(function(x) {
             v.forEach(function(y) {
                 console.log(
diff --git a/test/compress/properties.js b/test/compress/properties.js
index 376fb9e..29bdfe2 100644
--- a/test/compress/properties.js
+++ b/test/compress/properties.js
@@ -77,7 +77,7 @@ sub_properties: {
         a[3.14] = 3;
         a.if = 4;
         a["foo bar"] = 5;
-        a[0/0] = 6;
+        a[NaN] = 6;
         a[null] = 7;
         a[void 0] = 8;
     }

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