preserve ThisBinding for side_effects
authoralexlamsl <alexlamsl@gmail.com>
Tue, 16 Feb 2016 18:52:28 +0000 (02:52 +0800)
committerRichard van Velzen <rvanvelzen@experty.com>
Wed, 17 Feb 2016 18:34:01 +0000 (19:34 +0100)
lib/compress.js
lib/scope.js
test/compress/issue-782.js
test/compress/issue-973.js
test/compress/issue-976.js [new file with mode: 0644]

index df8975d..ed4f62f 100644 (file)
@@ -179,9 +179,9 @@ merge(Compressor.prototype, {
     // we shouldn't compress (1,func)(something) to
     // func(something) because that changes the meaning of
     // the func (becomes lexical instead of global).
-    function maintain_call_binding(parent, orig, val) {
+    function maintain_this_binding(parent, orig, val) {
         if (parent instanceof AST_Call && parent.expression === orig) {
-            if (val instanceof AST_PropAccess || (val instanceof AST_Symbol && val.name === "eval")) {
+            if (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name === "eval") {
                 return make_node(AST_Seq, orig, {
                     car: make_node(AST_Number, orig, {
                         value: 0
@@ -383,7 +383,7 @@ merge(Compressor.prototype, {
                 if (is_lvalue(node, parent)) return node;
 
                 // Remove var definition and return its value to the TreeTransformer to replace.
-                var value = maintain_call_binding(parent, node, var_decl.value);
+                var value = maintain_this_binding(parent, node, var_decl.value);
                 var_decl.value = null;
 
                 var_defs.splice(var_defs_index, 1);
@@ -2125,7 +2125,7 @@ merge(Compressor.prototype, {
         if (!compressor.option("side_effects"))
             return self;
         if (!self.car.has_side_effects(compressor)) {
-            return maintain_call_binding(compressor.parent(), self, self.cdr);
+            return maintain_this_binding(compressor.parent(), self, self.cdr);
         }
         if (compressor.option("cascade")) {
             if (self.car instanceof AST_Assign
@@ -2315,10 +2315,10 @@ merge(Compressor.prototype, {
                 if (ll.length > 1) {
                     if (ll[1]) {
                         compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
-                        return maintain_call_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
+                        return maintain_this_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
                     } else {
                         compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
-                        return maintain_call_binding(compressor.parent(), self, ll[0]);
+                        return maintain_this_binding(compressor.parent(), self, ll[0]);
                     }
                 }
             }
@@ -2327,10 +2327,10 @@ merge(Compressor.prototype, {
                 if (ll.length > 1) {
                     if (ll[1]) {
                         compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
-                        return maintain_call_binding(compressor.parent(), self, ll[0]);
+                        return maintain_this_binding(compressor.parent(), self, ll[0]);
                     } else {
                         compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
-                        return maintain_call_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
+                        return maintain_this_binding(compressor.parent(), self, self.right.evaluate(compressor)[0]);
                     }
                 }
             }
@@ -2555,10 +2555,10 @@ merge(Compressor.prototype, {
         if (cond.length > 1) {
             if (cond[1]) {
                 compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
-                return maintain_call_binding(compressor.parent(), self, self.consequent);
+                return maintain_this_binding(compressor.parent(), self, self.consequent);
             } else {
                 compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
-                return maintain_call_binding(compressor.parent(), self, self.alternative);
+                return maintain_this_binding(compressor.parent(), self, self.alternative);
             }
         }
         var negated = cond[0].negate(compressor);
index 20d9d73..ace2589 100644 (file)
@@ -194,6 +194,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
         }
         if (node instanceof AST_SymbolRef) {
             var name = node.name;
+            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;
+                }
+            }
             var sym = node.scope.find_variable(name);
             if (!sym) {
                 var g;
@@ -206,10 +211,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
                     globals.set(name, g);
                 }
                 node.thedef = g;
-                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;
-                }
                 if (func && name == "arguments") {
                     func.uses_arguments = true;
                 }
index 80b1493..2f72d1a 100644 (file)
@@ -1,10 +1,12 @@
 remove_redundant_sequence_items: {
     options = { side_effects: true };
     input: {
+        (0, 1, eval)();
         (0, 1, logThis)();
         (0, 1, _decorators.logThis)();
     }
     expect: {
+        (0, eval)();
         logThis();
         (0, _decorators.logThis)();
     }
@@ -13,10 +15,12 @@ remove_redundant_sequence_items: {
 dont_remove_this_binding_sequence: {
     options = { side_effects: true };
     input: {
+        (0, eval)();
         (0, logThis)();
         (0, _decorators.logThis)();
     }
     expect: {
+        (0, eval)();
         logThis();
         (0, _decorators.logThis)();
     }
index f2d4401..0e04092 100644 (file)
@@ -18,6 +18,11 @@ this_binding_conditionals: {
         (0 || a[b])();
         (0 || 1 && a[b])();
         (1 ? a[b] : 0)();
+
+        (1 && eval)();
+        (0 || eval)();
+        (0 || 1 && eval)();
+        (1 ? eval : 0)();
     }
     expect: {
         a();
@@ -34,6 +39,11 @@ this_binding_conditionals: {
         (0, a[b])();
         (0, a[b])();
         (0, a[b])();
+
+        (0, eval)();
+        (0, eval)();
+        (0, eval)();
+        (0, eval)();
     }
 }
 
@@ -44,26 +54,43 @@ this_binding_collapse_vars: {
     input: {
         var c = a; c();
         var d = a.b; d();
+        var e = eval; e();
     }
     expect: {
         a();
         (0, a.b)();
+        (0, eval)();
     }
 }
 
-eval_direct_calls: {
+this_binding_side_effects: {
     options = {
-        side_effects: true,
-        collapse_vars: true
-    }
+        side_effects : true
+    };
     input: {
-        (0, eval)('');
-
-        var fn = eval;
-        fn('');
+        (function (foo) {
+            (0, foo)();
+            (0, foo.bar)();
+            (0, eval)('console.log(foo);');
+        }());
+        (function (foo) {
+            var eval = console;
+            (0, foo)();
+            (0, foo.bar)();
+            (0, eval)('console.log(foo);');
+        }());
     }
     expect: {
-        (0, eval)('');
-        (0, eval)('');
+        (function (foo) {
+            foo();
+            (0, foo.bar)();
+            (0, eval)('console.log(foo);');
+        }());
+        (function (foo) {
+            var eval = console;
+            foo();
+            (0, foo.bar)();
+            (0, eval)('console.log(foo);');
+        }());
     }
 }
\ No newline at end of file
diff --git a/test/compress/issue-976.js b/test/compress/issue-976.js
new file mode 100644 (file)
index 0000000..dea3070
--- /dev/null
@@ -0,0 +1,88 @@
+eval_collapse_vars: {
+    options = {
+        collapse_vars:true, sequences:false, properties:true, dead_code:true, conditionals:true,
+        comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
+        keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
+    };
+    input: {
+        function f1() {
+            var e = 7;
+            var s = "abcdef";
+            var i = 2;
+            var eval = console.log.bind(console);
+            var x = s.charAt(i++);
+            var y = s.charAt(i++);
+            var z = s.charAt(i++);
+            eval(x, y, z, e);
+        }
+        function p1() { var a = foo(), b = bar(), eval = baz(); return a + b + eval; }
+        function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
+        (function f2(eval) {
+            var a = 2;
+            console.log(a - 5);
+            eval("console.log(a);");
+        })(eval);
+    }
+    expect: {
+        function f1() {
+            var e = 7,
+                s = "abcdef",
+                i = 2,
+                eval = console.log.bind(console),
+                x = s.charAt(i++),
+                y = s.charAt(i++),
+                z = s.charAt(i++);
+            eval(x, y, z, e);
+        }
+        function p1() { return foo() + bar() + baz(); }
+        function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); }
+        (function f2(eval) {
+            var a = 2;
+            console.log(a - 5);
+            eval("console.log(a);");
+        })(eval);
+    }
+}
+
+eval_unused: {
+    options = { unused: true, keep_fargs: false };
+    input: {
+        function f1(a, eval, c, d, e) {
+            return a('c') + eval;
+        }
+        function f2(a, b, c, d, e) {
+            return a + eval('c');
+        }
+        function f3(a, eval, c, d, e) {
+            return a + eval('c');
+        }
+    }
+    expect: {
+        function f1(a, eval) {
+            return a('c') + eval;
+        }
+        function f2(a, b, c, d, e) {
+            return a + eval('c');
+        }
+        function f3(a, eval, c, d, e) {
+            return a + eval('c');
+        }
+    }
+}
+
+eval_mangle: {
+    mangle = {
+    };
+    input: {
+        function f1(a, eval, c, d, e) {
+            return a('c') + eval;
+        }
+        function f2(a, b, c, d, e) {
+            return a + eval('c');
+        }
+        function f3(a, eval, c, d, e) {
+            return a + eval('c');
+        }
+    }
+    expect_exact: 'function f1(n,c,e,a,o){return n("c")+c}function f2(a,b,c,d,e){return a+eval("c")}function f3(a,eval,c,d,e){return a+eval("c")}'
+}