compress `AST_Sequence` within `AST_Call` (#3117)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 3 May 2018 11:14:56 +0000 (19:14 +0800)
committerGitHub <noreply@github.com>
Thu, 3 May 2018 11:14:56 +0000 (19:14 +0800)
lib/compress.js
test/compress/sequences.js

index 6029318..49abc54 100644 (file)
@@ -908,7 +908,7 @@ merge(Compressor.prototype, {
     // the func (becomes lexical instead of global).
     function maintain_this_binding(parent, orig, val) {
         if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
-            || parent instanceof AST_Call && parent.expression === orig
+            || parent.TYPE == "Call" && parent.expression === orig
                 && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
             return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
         }
@@ -4456,7 +4456,24 @@ merge(Compressor.prototype, {
         return self;
     });
 
-    OPT(AST_Call, function(self, compressor){
+    AST_Call.DEFMETHOD("lift_sequences", function(compressor) {
+        if (!compressor.option("sequences")) return this;
+        var exp = this.expression;
+        if (!(exp instanceof AST_Sequence)) return this;
+        var tail = exp.tail_node();
+        if (tail instanceof AST_PropAccess && !(this instanceof AST_New)) return this;
+        var expressions = exp.expressions.slice(0, -1);
+        var node = this.clone();
+        node.expression = tail;
+        expressions.push(node);
+        return make_sequence(this, expressions).optimize(compressor);
+    });
+
+    OPT(AST_Call, function(self, compressor) {
+        var seq = self.lift_sequences(compressor);
+        if (seq !== self) {
+            return seq;
+        }
         var exp = self.expression;
         var fn = exp;
         if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
@@ -4964,7 +4981,11 @@ merge(Compressor.prototype, {
         }
     });
 
-    OPT(AST_New, function(self, compressor){
+    OPT(AST_New, function(self, compressor) {
+        var seq = self.lift_sequences(compressor);
+        if (seq !== self) {
+            return seq;
+        }
         if (compressor.option("unsafe")) {
             var exp = self.expression;
             if (is_undeclared_ref(exp)) {
@@ -5020,14 +5041,12 @@ merge(Compressor.prototype, {
     });
 
     AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
-        if (compressor.option("sequences")) {
-            if (this.expression instanceof AST_Sequence) {
-                var x = this.expression.expressions.slice();
-                var e = this.clone();
-                e.expression = x.pop();
-                x.push(e);
-                return make_sequence(this, x).optimize(compressor);
-            }
+        if (compressor.option("sequences") && this.expression instanceof AST_Sequence) {
+            var x = this.expression.expressions.slice();
+            var e = this.clone();
+            e.expression = x.pop();
+            x.push(e);
+            return make_sequence(this, x).optimize(compressor);
         }
         return this;
     });
index 12acbcf..8aede12 100644 (file)
@@ -876,3 +876,59 @@ forin: {
     }
     expect_stdout: "PASS"
 }
+
+call: {
+    options = {
+        sequences: true,
+    }
+    input: {
+        var a = function() {
+            return this;
+        }();
+        function b() {
+            console.log("foo");
+        }
+        b.c = function() {
+            console.log(this === b ? "bar" : "baz");
+        };
+        (a, b)();
+        (a, b.c)();
+        (a, function() {
+            console.log(this === a);
+        })();
+        new (a, b)();
+        new (a, b.c)();
+        new (a, function() {
+            console.log(this === a);
+        })();
+    }
+    expect: {
+        var a = function() {
+            return this;
+        }();
+        function b() {
+            console.log("foo");
+        }
+        b.c = function() {
+            console.log(this === b ? "bar" : "baz");
+        },
+        a, b(),
+        (a, b.c)(),
+        a, function() {
+            console.log(this === a);
+        }(),
+        a, new b(),
+        a, new b.c(),
+        a, new function() {
+            console.log(this === a);
+        }();
+    }
+    expect_stdout: [
+        "foo",
+        "baz",
+        "true",
+        "foo",
+        "baz",
+        "false",
+    ]
+}