improve `--reduce-test` (#3722)
authorkzc <kzc@users.noreply.github.com>
Sat, 15 Feb 2020 20:22:33 +0000 (15:22 -0500)
committerGitHub <noreply@github.com>
Sat, 15 Feb 2020 20:22:33 +0000 (20:22 +0000)
- hoist body of functions and IIFEs
- simplify var declarations

test/input/reduce/unsafe_math.reduced.js
test/reduce.js

index ac2f189..1346042 100644 (file)
@@ -1,13 +1,10 @@
 var b = 0;
 
-function f0() {
-    var expr2 = (0 - 1 - .1 - .1).toString();
-    for (var key2 in expr2) {
-        --b;
-    }
-}
+var expr2 = (0 - 1 - .1 - .1).toString();
 
-var a_1 = f0();
+for (var key2 in expr2) {
+    --b;
+}
 
 console.log(b);
 // output: -19
index 20b1dd5..a5b1f51 100644 (file)
@@ -61,9 +61,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
 
             var parent = tt.parent();
 
-            // ignore call expressions
-            if (parent instanceof U.AST_Call && parent.expression === node) return;
-
             // ensure that the _permute prop is a number.
             // can not use `node.start._permute |= 0;` as it will erase fractional part.
             if (typeof node.start._permute === "undefined") node.start._permute = 0;
@@ -128,11 +125,43 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
                 var expr = [
                     node.expression,
                     node.args[0],
-                ][ node.start._permute++ % 2 ];
+                    null,  // intentional
+                ][ ((node.start._permute += step) * steps | 0) % 3 ];
                 if (expr) {
                     CHANGED = true;
                     return expr;
                 }
+                if (node.expression instanceof U.AST_Function) {
+                    // hoist and return expressions from the IIFE function expression
+                    var body = node.expression.body;
+                    node.expression.body = [];
+                    var seq = [];
+                    body.forEach(function(node) {
+                        var expr = expr instanceof U.AST_Exit ? node.value : node.body;
+                        if (expr instanceof U.AST_Node && !is_statement(expr)) {
+                            // collect expressions from each statements' body
+                            seq.push(expr);
+                        }
+                    });
+                    CHANGED = true;
+                    return to_sequence(seq);
+                }
+            }
+            else if (node instanceof U.AST_Defun) {
+                switch (((node.start._permute += step) * steps | 0) % 2) {
+                  case 0:
+                    CHANGED = true;
+                    return List.skip;
+                  case 1:
+                    if (!has_exit(node)) {
+                        // hoist function declaration body
+                        var body = node.body;
+                        node.body = [];
+                        body.push(node); // retain function with empty body to be dropped later
+                        CHANGED = true;
+                        return List.splice(body);
+                    }
+                }
             }
             else if (node instanceof U.AST_DWLoop) {
                 var expr = [
@@ -212,6 +241,18 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
                     return expr;
                 }
             }
+            else if (node instanceof U.AST_SimpleStatement) {
+                if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) {
+                    // hoist simple statement IIFE function expression body
+                    node.start._permute++;
+                    if (!has_exit(node.body.expression)) {
+                        var body = node.body.expression.body;
+                        node.body.expression.body = [];
+                        CHANGED = true;
+                        return List.splice(body);
+                    }
+                }
+            }
             else if (node instanceof U.AST_Switch) {
                 var expr = [
                     node.expression,                         // switch expression
@@ -256,6 +297,14 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
                 CHANGED = true;
                 return node.expression;
             }
+            else if (node instanceof U.AST_Var) {
+                if (node.definitions.length == 1 && node.definitions[0].value) {
+                    // first declaration value
+                    node.start._permute++;
+                    CHANGED = true;
+                    return to_statement(node.definitions[0].value);
+                }
+            }
             else if (node instanceof U.AST_LabeledStatement) {
                 if (node.body instanceof U.AST_Statement
                     && !has_loopcontrol(node.body, node.body, node)) {
@@ -291,7 +340,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
             }
 
             // replace this node
-            var newNode = new U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], {
+            var newNode = U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], {
                 expression: true,
             });
             if (is_statement(node)) {
@@ -404,6 +453,21 @@ function to_comment(value) {
     return ("" + value).replace(/\n/g, "\n// ");
 }
 
+function has_exit(fn) {
+    var found = false;
+    var tw = new U.TreeWalker(function(node) {
+        if (found) return found;
+        if (node instanceof U.AST_Exit) {
+            return found = true;
+        }
+        if (node instanceof U.AST_Scope && node !== fn) {
+            return true; // don't descend into nested functions
+        }
+    });
+    fn.walk(tw);
+    return found;
+}
+
 function has_loopcontrol(body, loop, label) {
     var found = false;
     var tw = new U.TreeWalker(function(node) {
@@ -426,6 +490,24 @@ function is_statement(node) {
     return node instanceof U.AST_Statement && !(node instanceof U.AST_Function);
 }
 
+function merge_sequence(array, node) {
+    if (node instanceof U.AST_Sequence) {
+        array.push.apply(array, node.expressions);
+    } else {
+        array.push(node);
+    }
+    return array;
+}
+
+function to_sequence(expressions) {
+    if (expressions.length == 0) return new U.AST_Number({value: 0, start: {}});
+    if (expressions.length == 1) return expressions[0];
+    return new U.AST_Sequence({
+        expressions: expressions.reduce(merge_sequence, []),
+        start: {},
+    });
+}
+
 function to_statement(node) {
     return is_statement(node) ? node : new U.AST_SimpleStatement({
         body: node,