enhance `evaluate` (#3549)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 29 Oct 2019 11:51:55 +0000 (19:51 +0800)
committerGitHub <noreply@github.com>
Tue, 29 Oct 2019 11:51:55 +0000 (19:51 +0800)
lib/compress.js
test/compress/evaluate.js

index d37108d..ad47621 100644 (file)
@@ -3019,7 +3019,26 @@ merge(Compressor.prototype, {
         });
         def(AST_Call, function(compressor, cached, depth) {
             var exp = this.expression;
-            if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
+            if (exp instanceof AST_SymbolRef) {
+                var fn = exp.fixed_value();
+                if (!(fn instanceof AST_Lambda)) return this;
+                if (fn.name && fn.name.definition().recursive_refs > 0) return this;
+                if (fn.body.length != 1 || !fn.is_constant_expression()) return this;
+                var stat = fn.body[0];
+                if (!(stat instanceof AST_Return)) return this;
+                var args = eval_args(this.args);
+                if (!args) return this;
+                fn.argnames.forEach(function(sym, i) {
+                    var value = args[i];
+                    sym.definition().references.forEach(function(node) {
+                        node._eval = function() {
+                            return value;
+                        };
+                        cached.push(node);
+                    });
+                });
+                return stat.value ? stat.value._eval(compressor, cached, depth) : undefined;
+            } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
                 var key = exp.property;
                 if (key instanceof AST_Node) {
                     key = key._eval(compressor, cached, depth);
@@ -3037,13 +3056,8 @@ merge(Compressor.prototype, {
                     var native_fn = native_fns[val.constructor.name];
                     if (!native_fn || !native_fn[key]) return this;
                 }
-                var args = [];
-                for (var i = 0; i < this.args.length; i++) {
-                    var arg = this.args[i];
-                    var value = arg._eval(compressor, cached, depth);
-                    if (arg === value) return this;
-                    args.push(value);
-                }
+                var args = eval_args(this.args);
+                if (!args) return this;
                 if (key == "replace" && typeof args[1] == "function") return this;
                 try {
                     return val[key].apply(val, args);
@@ -3057,6 +3071,17 @@ merge(Compressor.prototype, {
                 }
             }
             return this;
+
+            function eval_args(args) {
+                var values = [];
+                for (var i = 0; i < args.length; i++) {
+                    var arg = args[i];
+                    var value = arg._eval(compressor, cached, depth);
+                    if (arg === value) return;
+                    values.push(value);
+                }
+                return values;
+            }
         });
         def(AST_New, return_this);
     })(function(node, func) {
@@ -3406,19 +3431,19 @@ merge(Compressor.prototype, {
         def(AST_Lambda, function(scope) {
             var self = this;
             var result = true;
-            var inner_scopes = [];
+            var scopes = [];
             self.walk(new TreeWalker(function(node, descend) {
                 if (!result) return true;
                 if (node instanceof AST_Catch) {
-                    inner_scopes.push(node.argname.scope);
+                    scopes.push(node.argname.scope);
                     descend();
-                    inner_scopes.pop();
+                    scopes.pop();
                     return true;
                 }
-                if (node instanceof AST_Scope && node !== self) {
-                    inner_scopes.push(node);
+                if (node instanceof AST_Scope) {
+                    scopes.push(node);
                     descend();
-                    inner_scopes.pop();
+                    scopes.pop();
                     return true;
                 }
                 if (node instanceof AST_SymbolRef) {
@@ -3427,16 +3452,15 @@ merge(Compressor.prototype, {
                         return true;
                     }
                     var def = node.definition();
-                    if (!self.variables.has(def.name) && !member(def.scope, inner_scopes)) {
-                        if (scope) {
-                            var scope_def = scope.find_variable(node);
-                            if (def.undeclared ? !scope_def : scope_def === def) {
-                                result = "f";
-                                return true;
-                            }
+                    if (member(def.scope, scopes)) return true;
+                    if (scope) {
+                        var scope_def = scope.find_variable(node);
+                        if (def.undeclared ? !scope_def : scope_def === def) {
+                            result = "f";
+                            return true;
                         }
-                        result = false;
                     }
+                    result = false;
                     return true;
                 }
             }));
index 0ac117f..771bd4f 100644 (file)
@@ -1757,3 +1757,84 @@ issue_3387_2: {
     }
     expect_stdout: "NaN"
 }
+
+simple_function_1: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        function sum(a, b) {
+            return a + b;
+        }
+        console.log(sum(1, 2) * sum(3, 4));
+    }
+    expect: {
+        console.log(21);
+    }
+    expect_stdout: "21"
+}
+
+simple_function_2: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var sum = function(a, b) {
+            return a + b;
+        }
+        console.log(sum(1, 2) * sum(3, 4));
+    }
+    expect: {
+        console.log(21);
+    }
+    expect_stdout: "21"
+}
+
+recursive_function_1: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        function factorial(a) {
+            return a > 0 ? a * factorial(a - 1) : 1;
+        }
+        console.log(factorial(5));
+    }
+    expect: {
+        console.log(function factorial(a) {
+            return a > 0 ? a * factorial(a - 1) : 1;
+        }(5));
+    }
+    expect_stdout: "120"
+}
+
+recursive_function_2: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var factorial = function(a) {
+            return a > 0 ? a * factorial(a - 1) : 1;
+        }
+        console.log(factorial(5));
+    }
+    expect: {
+        var factorial = function(a) {
+            return a > 0 ? a * factorial(a - 1) : 1;
+        }
+        console.log(factorial(5));
+    }
+    expect_stdout: "120"
+}