improve `pure_getters` (#1786)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 6 Apr 2017 03:18:59 +0000 (11:18 +0800)
committerGitHub <noreply@github.com>
Thu, 6 Apr 2017 03:18:59 +0000 (11:18 +0800)
- property access to `null` & `undefined` always has side effects
- utilise `reduce_vars` to determine safe property access
- may-be cases treated as side effects unless `unsafe`

lib/compress.js
test/compress/collapse_vars.js
test/compress/pure_getters.js [new file with mode: 0644]
test/ufuzz.json

index ef7f044..22c79b8 100644 (file)
@@ -1160,6 +1160,26 @@ merge(Compressor.prototype, {
                 && !node.expression.has_side_effects(compressor);
     }
 
+    (function(def) {
+        def(AST_Node, return_false);
+        def(AST_Null, return_true);
+        def(AST_Undefined, return_true);
+        def(AST_UnaryPrefix, function() {
+            return this.operator == "void";
+        });
+        def(AST_PropAccess, function(compressor) {
+            return !compressor.option("unsafe");
+        });
+        def(AST_SymbolRef, function(compressor) {
+            if (this.is_undefined) return true;
+            if (compressor.option("unsafe")) return false;
+            var fixed = this.fixed_value();
+            return !fixed || fixed.may_eq_null(compressor);
+        });
+    })(function(node, func) {
+        node.DEFMETHOD("may_eq_null", func);
+    });
+
     /* -----[ boolean/negation helpers ]----- */
 
     // methods to determine whether an expression has a boolean result type
@@ -1688,7 +1708,7 @@ merge(Compressor.prototype, {
                 || this.expression.has_side_effects(compressor);
         });
         def(AST_SymbolRef, function(compressor){
-            return this.global() && this.undeclared();
+            return this.undeclared();
         });
         def(AST_Object, function(compressor){
             return any(this.properties, compressor);
@@ -1701,16 +1721,15 @@ merge(Compressor.prototype, {
         });
         def(AST_Dot, function(compressor){
             if (!compressor.option("pure_getters")) return true;
-            return this.expression.has_side_effects(compressor);
+            return this.expression.may_eq_null(compressor)
+                || this.expression.has_side_effects(compressor);
         });
         def(AST_Sub, function(compressor){
             if (!compressor.option("pure_getters")) return true;
-            return this.expression.has_side_effects(compressor)
+            return this.expression.may_eq_null(compressor)
+                || this.expression.has_side_effects(compressor)
                 || this.property.has_side_effects(compressor);
         });
-        def(AST_PropAccess, function(compressor){
-            return !compressor.option("pure_getters");
-        });
         def(AST_Seq, function(compressor){
             return this.car.has_side_effects(compressor)
                 || this.cdr.has_side_effects(compressor);
@@ -2275,10 +2294,12 @@ merge(Compressor.prototype, {
         });
         def(AST_Dot, function(compressor, first_in_statement){
             if (!compressor.option("pure_getters")) return this;
+            if (this.expression.may_eq_null(compressor)) return this;
             return this.expression.drop_side_effect_free(compressor, first_in_statement);
         });
         def(AST_Sub, function(compressor, first_in_statement){
             if (!compressor.option("pure_getters")) return this;
+            if (this.expression.may_eq_null(compressor)) return this;
             var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
             if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
             var property = this.property.drop_side_effect_free(compressor);
@@ -3509,7 +3530,6 @@ merge(Compressor.prototype, {
         // testing against !self.scope.uses_with first is an optimization
         if (compressor.option("screw_ie8")
             && self.undeclared()
-            && !isLHS(self, compressor.parent())
             && (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
             switch (self.name) {
               case "undefined":
index 4107707..2264783 100644 (file)
@@ -1573,6 +1573,7 @@ var_side_effects_3: {
     options = {
         collapse_vars: true,
         pure_getters: true,
+        unsafe: true,
     }
     input: {
         var print = console.log.bind(console);
diff --git a/test/compress/pure_getters.js b/test/compress/pure_getters.js
new file mode 100644 (file)
index 0000000..338f863
--- /dev/null
@@ -0,0 +1,85 @@
+side_effects: {
+    options = {
+        pure_getters: true,
+        reduce_vars: false,
+        side_effects: true,
+        toplevel: true,
+        unsafe: false,
+    }
+    input: {
+        var a, b = null, c = {};
+        a.prop;
+        b.prop;
+        c.prop;
+        d.prop;
+        null.prop;
+        (void 0).prop;
+        undefined.prop;
+    }
+    expect: {
+        var a, b = null, c = {};
+        a.prop;
+        b.prop;
+        c.prop;
+        d.prop;
+        null.prop;
+        (void 0).prop;
+        (void 0).prop;
+    }
+}
+
+side_effects_reduce_vars: {
+    options = {
+        pure_getters: true,
+        reduce_vars: true,
+        side_effects: true,
+        toplevel: true,
+        unsafe: false,
+    }
+    input: {
+        var a, b = null, c = {};
+        a.prop;
+        b.prop;
+        c.prop;
+        d.prop;
+        null.prop;
+        (void 0).prop;
+        undefined.prop;
+    }
+    expect: {
+        var a, b = null, c = {};
+        a.prop;
+        b.prop;
+        d.prop;
+        null.prop;
+        (void 0).prop;
+        (void 0).prop;
+    }
+}
+
+side_effects_unsafe: {
+    options = {
+        pure_getters: true,
+        reduce_vars: false,
+        side_effects: true,
+        toplevel: true,
+        unsafe: true,
+    }
+    input: {
+        var a, b = null, c = {};
+        a.prop;
+        b.prop;
+        c.prop;
+        d.prop;
+        null.prop;
+        (void 0).prop;
+        undefined.prop;
+    }
+    expect: {
+        var a, b = null, c = {};
+        d;
+        null.prop;
+        (void 0).prop;
+        (void 0).prop;
+    }
+}
index fd1084a..4523795 100644 (file)
@@ -35,7 +35,6 @@
         "compress": {
             "keep_fargs": false,
             "passes": 3,
-            "pure_getters": true,
             "warnings": false
         }
     }