enhance `inline` (#5226)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 21 Dec 2021 05:03:11 +0000 (05:03 +0000)
committerGitHub <noreply@github.com>
Tue, 21 Dec 2021 05:03:11 +0000 (13:03 +0800)
15 files changed:
README.md
lib/ast.js
lib/compress.js
test/compress/awaits.js
test/compress/classes.js
test/compress/collapse_vars.js
test/compress/const.js
test/compress/default-values.js
test/compress/destructured.js
test/compress/functions.js
test/compress/if_return.js
test/compress/keep_fargs.js
test/compress/reduce_vars.js
test/compress/spreads.js
test/compress/yields.js

index 8fbe07c..048de63 100644 (file)
--- a/README.md
+++ b/README.md
@@ -707,7 +707,8 @@ to be `false` and all symbol names will be omitted.
   - `1` — inline simple functions
   - `2` — inline functions with arguments
   - `3` — inline functions with arguments and variables
-  - `true` — same as `3`
+  - `4` — inline functions with arguments, variables and statements
+  - `true` — same as `4`
 
 - `join_vars` (default: `true`) — join consecutive `var` statements
 
index 1213852..7632348 100644 (file)
@@ -1977,27 +1977,27 @@ var AST_Atom = DEFNODE("Atom", null, {
 
 var AST_Null = DEFNODE("Null", null, {
     $documentation: "The `null` atom",
-    value: null
+    value: null,
 }, AST_Atom);
 
 var AST_NaN = DEFNODE("NaN", null, {
     $documentation: "The impossible value",
-    value: 0/0
+    value: 0/0,
 }, AST_Atom);
 
 var AST_Undefined = DEFNODE("Undefined", null, {
     $documentation: "The `undefined` value",
-    value: function(){}()
+    value: function(){}(),
 }, AST_Atom);
 
 var AST_Hole = DEFNODE("Hole", null, {
     $documentation: "A hole in an array",
-    value: function(){}()
+    value: function(){}(),
 }, AST_Atom);
 
 var AST_Infinity = DEFNODE("Infinity", null, {
     $documentation: "The `Infinity` value",
-    value: 1/0
+    value: 1/0,
 }, AST_Atom);
 
 var AST_Boolean = DEFNODE("Boolean", null, {
@@ -2009,12 +2009,12 @@ var AST_Boolean = DEFNODE("Boolean", null, {
 
 var AST_False = DEFNODE("False", null, {
     $documentation: "The `false` atom",
-    value: false
+    value: false,
 }, AST_Boolean);
 
 var AST_True = DEFNODE("True", null, {
     $documentation: "The `true` atom",
-    value: true
+    value: true,
 }, AST_Boolean);
 
 /* -----[ TreeWalker ]----- */
index 949abac..e4748a3 100644 (file)
@@ -121,7 +121,7 @@ function Compressor(options, false_by_default) {
             });
         }
     }
-    if (this.options["inline"] === true) this.options["inline"] = 3;
+    if (this.options["inline"] === true) this.options["inline"] = 4;
     this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) {
         if (lambda.length_read) return false;
         var name = lambda.name;
@@ -1646,9 +1646,7 @@ Compressor.prototype.compress = function(node) {
     function make_node_from_constant(val, orig) {
         switch (typeof val) {
           case "string":
-            return make_node(AST_String, orig, {
-                value: val
-            });
+            return make_node(AST_String, orig, { value: val });
           case "number":
             if (isNaN(val)) return make_node(AST_NaN, orig);
             if (isFinite(val)) {
@@ -1659,7 +1657,7 @@ Compressor.prototype.compress = function(node) {
             }
             return val < 0 ? make_node(AST_UnaryPrefix, orig, {
                 operator: "-",
-                expression: make_node(AST_Infinity, orig)
+                expression: make_node(AST_Infinity, orig),
             }) : make_node(AST_Infinity, orig);
           case "boolean":
             return make_node(val ? AST_True : AST_False, orig);
@@ -1667,7 +1665,7 @@ Compressor.prototype.compress = function(node) {
             return make_node(AST_Undefined, orig);
           default:
             if (val === null) {
-                return make_node(AST_Null, orig, { value: null });
+                return make_node(AST_Null, orig);
             }
             if (val instanceof RegExp) {
                 return make_node(AST_RegExp, orig, { value: val });
@@ -1816,6 +1814,9 @@ Compressor.prototype.compress = function(node) {
     }
 
     function tighten_body(statements, compressor) {
+        var in_lambda = last_of(compressor, function(node) {
+            return node instanceof AST_Lambda;
+        });
         var in_loop, in_try, scope;
         find_loop_scope_try();
         var changed, last_changed, max_iter = 10;
@@ -1832,22 +1833,37 @@ Compressor.prototype.compress = function(node) {
                 if (handle_if_return(statements, compressor)) changed = 3;
                 if (!changed && last_changed == 3) break;
             }
-            if (compressor.sequences_limit > 0) {
-                if (sequencesize(statements, compressor)) changed = 4;
+            if (compressor.option("inline") >= 4) {
+                if (inline_last_iife(statements, compressor)) changed = 4;
                 if (!changed && last_changed == 4) break;
-                if (sequencesize_2(statements, compressor)) changed = 5;
+            }
+            if (compressor.sequences_limit > 0) {
+                if (sequencesize(statements, compressor)) changed = 5;
                 if (!changed && last_changed == 5) break;
+                if (sequencesize_2(statements, compressor)) changed = 6;
+                if (!changed && last_changed == 6) break;
             }
             if (compressor.option("join_vars")) {
-                if (join_consecutive_vars(statements)) changed = 6;
-                if (!changed && last_changed == 6) break;
+                if (join_consecutive_vars(statements)) changed = 7;
+                if (!changed && last_changed == 7) break;
             }
             if (compressor.option("collapse_vars")) {
-                if (collapse(statements, compressor)) changed = 7;
+                if (collapse(statements, compressor)) changed = 8;
             }
         } while (changed && max_iter-- > 0);
         return statements;
 
+        function last_of(compressor, predicate) {
+            var block = compressor.self(), stat, level = 0;
+            do {
+                do {
+                    if (predicate(block)) return true;
+                    block = compressor.parent(level++);
+                } while (block instanceof AST_If && (stat = block));
+            } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
+                && is_last_statement(block.body, stat));
+        }
+
         function find_loop_scope_try() {
             var node = compressor.self(), level = 0;
             do {
@@ -3135,11 +3151,7 @@ Compressor.prototype.compress = function(node) {
 
         function handle_if_return(statements, compressor) {
             var changed = false;
-            var self = compressor.self();
             var parent = compressor.parent();
-            var in_lambda = last_of(function(node) {
-                return node instanceof AST_Lambda;
-            });
             var in_iife = in_lambda && parent && parent.TYPE == "Call";
             var multiple_if_returns = has_multiple_if_returns(statements);
             for (var i = statements.length; --i >= 0;) {
@@ -3163,9 +3175,7 @@ Compressor.prototype.compress = function(node) {
                             body = stat.value.clone();
                             body.expressions[body.length - 1] = tail.expression;
                         }
-                        statements[i] = make_node(AST_SimpleStatement, stat, {
-                            body: body,
-                        });
+                        statements[i] = make_node(AST_SimpleStatement, stat, { body: body });
                         continue;
                     }
                 }
@@ -3179,11 +3189,9 @@ Compressor.prototype.compress = function(node) {
                         stat.condition = stat.condition.negate(compressor);
                         var body = as_statement_array_with_return(stat.body, ab);
                         stat.body = make_node(AST_BlockStatement, stat, {
-                            body: as_statement_array(stat.alternative).concat(extract_functions())
-                        });
-                        stat.alternative = make_node(AST_BlockStatement, stat, {
-                            body: body
+                            body: as_statement_array(stat.alternative).concat(extract_functions()),
                         });
+                        stat.alternative = make_node(AST_BlockStatement, stat, { body: body });
                         statements[i] = stat;
                         statements[i] = stat.transform(compressor);
                         continue;
@@ -3209,12 +3217,10 @@ Compressor.prototype.compress = function(node) {
                         changed = true;
                         stat = stat.clone();
                         stat.body = make_node(AST_BlockStatement, stat.body, {
-                            body: as_statement_array(stat.body).concat(extract_functions())
+                            body: as_statement_array(stat.body).concat(extract_functions()),
                         });
                         var body = as_statement_array_with_return(stat.alternative, alt);
-                        stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
-                            body: body
-                        });
+                        stat.alternative = make_node(AST_BlockStatement, stat.alternative, { body: body });
                         statements[i] = stat;
                         statements[i] = stat.transform(compressor);
                         continue;
@@ -3222,14 +3228,16 @@ Compressor.prototype.compress = function(node) {
 
                     if (compressor.option("typeofs")) {
                         if (ab && !alt) {
-                            mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
-                                body: statements.slice(i + 1)
-                            }));
+                            var stats = make_node(AST_BlockStatement, compressor.self(), {
+                                body: statements.slice(i + 1),
+                            });
+                            mark_locally_defined(stat.condition, null, stats);
                         }
                         if (!ab && alt) {
-                            mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
-                                body: statements.slice(i + 1)
-                            }));
+                            var stats = make_node(AST_BlockStatement, compressor.self(), {
+                                body: statements.slice(i + 1),
+                            });
+                            mark_locally_defined(stat.condition, stats);
                         }
                     }
                 }
@@ -3243,9 +3251,7 @@ Compressor.prototype.compress = function(node) {
                     if (!value && !stat.alternative
                         && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
                         changed = true;
-                        statements[i] = make_node(AST_SimpleStatement, stat.condition, {
-                            body: stat.condition
-                        });
+                        statements[i] = make_node(AST_SimpleStatement, stat.condition, { body: stat.condition });
                         continue;
                     }
                     //---
@@ -3263,9 +3269,7 @@ Compressor.prototype.compress = function(node) {
                     if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
                         changed = true;
                         stat = stat.clone();
-                        stat.alternative = make_node(AST_Return, stat, {
-                            value: null
-                        });
+                        stat.alternative = make_node(AST_Return, stat, { value: null });
                         statements.splice(i, 1, stat.transform(compressor));
                         continue;
                     }
@@ -3284,10 +3288,8 @@ Compressor.prototype.compress = function(node) {
                         stat.alternative = make_node(AST_BlockStatement, next, {
                             body: [
                                 next,
-                                make_node(AST_Return, next, {
-                                    value: null
-                                })
-                            ]
+                                make_node(AST_Return, next, { value: null }),
+                            ],
                         });
                         statements.splice(i, 1, stat.transform(compressor));
                         statements.splice(j, 1);
@@ -3312,19 +3314,8 @@ Compressor.prototype.compress = function(node) {
                 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
             }
 
-            function last_of(predicate) {
-                var block = self, stat, level = 0;
-                do {
-                    do {
-                        if (predicate(block)) return true;
-                        block = compressor.parent(level++);
-                    } while (block instanceof AST_If && (stat = block));
-                } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
-                    && is_last_statement(block.body, stat));
-            }
-
             function match_target(target) {
-                return last_of(function(node) {
+                return last_of(compressor, function(node) {
                     return node === target;
                 });
             }
@@ -3426,6 +3417,25 @@ Compressor.prototype.compress = function(node) {
             return statements.length != len;
         }
 
+        function inline_last_iife(statements, compressor) {
+            if (!in_lambda) return false;
+            var index = statements.length - 1;
+            var stat = statements[index];
+            if (!(stat instanceof AST_SimpleStatement)) return false;
+            var body = stat.body;
+            if (body instanceof AST_UnaryPrefix) {
+                if (unary_side_effects[body.operator]) return false;
+                body = body.expression;
+            }
+            var inlined = make_node(AST_UnaryPrefix, stat, {
+                operator: "void",
+                expression: body,
+            }).try_inline(compressor, scope);
+            if (!inlined) return false;
+            statements[index] = inlined;
+            return true;
+        }
+
         function sequencesize(statements, compressor) {
             if (statements.length < 2) return;
             var seq = [], n = 0;
@@ -9351,6 +9361,26 @@ Compressor.prototype.compress = function(node) {
         return avoid.length && makePredicate(avoid);
     }
 
+    function safe_from_await_yield(fn, avoid) {
+        if (!avoid) return true;
+        var safe = true;
+        var tw = new TreeWalker(function(node) {
+            if (!safe) return true;
+            if (node instanceof AST_Scope) {
+                if (node === fn) return;
+                if (is_arrow(node)) {
+                    for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
+                } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
+                    safe = false;
+                }
+                return true;
+            }
+            if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
+        });
+        fn.walk(tw);
+        return safe;
+    }
+
     OPT(AST_Call, function(self, compressor) {
         var exp = self.expression;
         var terminated = trim_optional_chain(self, compressor);
@@ -9681,7 +9711,7 @@ Compressor.prototype.compress = function(node) {
             if (exp === fn
                 && !fn.name
                 && (!value || value.is_constant_expression())
-                && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
+                && safe_from_await_yield(fn, avoid_await_yield(compressor.find_parent(AST_Scope)))) {
                 return make_sequence(self, convert_args(value)).optimize(compressor);
             }
         }
@@ -9693,7 +9723,6 @@ Compressor.prototype.compress = function(node) {
                 && !(fn.name && fn instanceof AST_LambdaExpression)
                 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
                     && fn.is_constant_expression(find_scope(compressor)))
-                && !has_spread
                 && (value = can_flatten_body(stat))
                 && !fn.contains_this()) {
                 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
@@ -9761,7 +9790,7 @@ Compressor.prototype.compress = function(node) {
                 && all(fn.body, is_empty)
                 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
                 && !(is_arrow(fn) && fn.value)
-                && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
+                && safe_from_await_yield(fn, avoid_await_yield(compressor.find_parent(AST_Scope)))) {
                 return make_sequence(self, convert_args()).optimize(compressor);
             }
         }
@@ -9869,27 +9898,6 @@ Compressor.prototype.compress = function(node) {
             return args;
         }
 
-        function safe_from_await_yield(node, scope) {
-            var avoid = avoid_await_yield(scope);
-            if (!avoid) return true;
-            var safe = true;
-            var tw = new TreeWalker(function(node) {
-                if (!safe) return true;
-                if (node instanceof AST_Scope) {
-                    if (node === fn) return;
-                    if (is_arrow(node)) {
-                        for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
-                    } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
-                        safe = false;
-                    }
-                    return true;
-                }
-                if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
-            });
-            node.walk(tw);
-            return safe;
-        }
-
         function noop_value() {
             return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
         }
@@ -9941,7 +9949,7 @@ Compressor.prototype.compress = function(node) {
         }
 
         function can_substitute_directly() {
-            if (has_default || has_destructured || var_assigned || fn.rest) return;
+            if (has_default || has_destructured || has_spread || var_assigned || fn.rest) return;
             if (compressor.option("inline") < 2 && fn.argnames.length) return;
             if (!fn.variables.all(function(def) {
                 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
@@ -10065,7 +10073,7 @@ Compressor.prototype.compress = function(node) {
             } while (!(scope instanceof AST_Scope));
             insert = scope.body.indexOf(child) + 1;
             if (!insert) return false;
-            if (!safe_from_await_yield(fn, scope)) return false;
+            if (!safe_from_await_yield(fn, avoid_await_yield(scope))) return false;
             var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
             if (scope instanceof AST_Toplevel) {
                 if (compressor.toplevel.vars) {
@@ -10227,7 +10235,7 @@ Compressor.prototype.compress = function(node) {
         function flatten_fn() {
             var decls = [];
             var expressions = [];
-            if (has_default > 1 || has_destructured || fn.rest) {
+            if (has_default > 1 || has_destructured || has_spread || fn.rest) {
                 flatten_destructured(decls, expressions);
             } else {
                 flatten_args(decls, expressions);
@@ -12676,10 +12684,134 @@ Compressor.prototype.compress = function(node) {
         }
     });
 
+    (function(def) {
+        def(AST_Node, noop);
+        function process(sym, argname) {
+            argname.definition().orig.push(sym);
+        }
+        def(AST_Call, function(compressor, scope) {
+            if (compressor.option("inline") < 4) return;
+            var call = this;
+            if (call.is_expr_pure(compressor)) return;
+            var fn = call.expression;
+            if (!(fn instanceof AST_Function)) return;
+            if (fn.name) return;
+            if (fn.uses_arguments) return;
+            if (fn.contains_this()) return;
+            if (!scope) scope = compressor.find_parent(AST_Scope);
+            var names = scope.var_names();
+            if (!fn.variables.all(function(def, name) {
+                if (!names.has(name)) return true;
+                if (name != "arguments") return false;
+                if (scope.uses_arguments) return false;
+                return def.references.length == def.replaced;
+            })) return;
+            var safe = true;
+            fn.each_argname(function(argname) {
+                if (!all(argname.definition().orig, function(sym) {
+                    return !(sym instanceof AST_SymbolDefun);
+                })) safe = false;
+            });
+            if (!safe) return;
+            if (!safe_from_await_yield(fn, avoid_await_yield(scope))) return;
+            fn.functions.each(function(def, name) {
+                scope.functions.set(name, def);
+            });
+            fn.variables.each(function(def, name) {
+                scope.variables.set(name, def);
+                def.single_use = false;
+            });
+            if (fn.variables.has("NaN")) scope.transform(new TreeTransformer(function(node) {
+                if (node instanceof AST_NaN) return make_node(AST_Binary, node, {
+                    operator: "/",
+                    left: make_node(AST_Number, node, { value: 0 }),
+                    right: make_node(AST_Number, node, { value: 0 }),
+                });
+                if (node instanceof AST_Scope && node !== scope) return node;
+            }));
+            var body = [];
+            if (fn.rest || !all(fn.argnames, function(argname) {
+                return argname instanceof AST_SymbolFunarg;
+            }) || !all(call.args, function(arg) {
+                return !(arg instanceof AST_Spread);
+            })) {
+                body.push(make_node(AST_Var, call, {
+                    definitions: [ make_node(AST_VarDef, call, {
+                        name: make_node(AST_DestructuredArray, call, {
+                            elements: fn.argnames.map(function(argname) {
+                                if (argname.unused) return make_node(AST_Hole, argname);
+                                return argname.convert_symbol(AST_SymbolVar, process);
+                            }),
+                            rest: fn.rest && fn.rest.convert_symbol(AST_SymbolVar, process),
+                        }),
+                        value: make_node(AST_Array, call, { elements: call.args.slice() }),
+                    }) ],
+                }));
+            } else {
+                var values = call.args.slice();
+                fn.argnames.forEach(function(argname) {
+                    var value = values.shift();
+                    if (argname.unused) {
+                        if (value) body.push(make_node(AST_SimpleStatement, call, { body: value }));
+                        return;
+                    }
+                    body.push(make_node(AST_Var, call, {
+                        definitions: [ make_node(AST_VarDef, call, {
+                            name: argname.convert_symbol(AST_SymbolVar, process),
+                            value: value || make_node(AST_Undefined, call).optimize(compressor),
+                        }) ],
+                    }));
+                });
+                if (values.length) body.push(make_node(AST_SimpleStatement, call, {
+                    body: make_sequence(call, values),
+                }));
+            }
+            return make_node(AST_BlockStatement, call, {
+                body: body.concat(fn.body, make_node(AST_Return, call, { value: null })),
+            });
+        });
+        def(AST_New, noop);
+        def(AST_Sequence, function(compressor, scope) {
+            var inlined = this.tail_node().try_inline(compressor, scope);
+            if (inlined) return make_node(AST_BlockStatement, this, {
+                body: [
+                    make_node(AST_SimpleStatement, this, {
+                        body: make_sequence(this, this.expressions.slice(0, -1)),
+                    }),
+                    inlined,
+                ],
+            });
+        });
+        def(AST_UnaryPrefix, function(compressor, scope) {
+            var self = this;
+            var op = self.operator;
+            if (unary_side_effects[op]) return;
+            var inlined = self.expression.try_inline(compressor, scope);
+            if (!inlined) return;
+            scan_local_returns(inlined, function(node) {
+                var value = node.value;
+                if (op == "void") {
+                    if (!value) return;
+                    if (is_undefined(value)) return;
+                }
+                node.value = make_node(AST_UnaryPrefix, self, {
+                    operator: op,
+                    expression: value || make_node(AST_Undefined, node),
+                }).optimize(compressor);
+            });
+            return inlined;
+        })
+    })(function(node, func) {
+        node.DEFMETHOD("try_inline", func);
+    });
+
     OPT(AST_Return, function(self, compressor) {
+        var value = self.value;
+        if (!value) return self;
+        var inlined = value.try_inline(compressor);
+        if (inlined) return inlined;
         if (compressor.option("side_effects")
-            && self.value
-            && is_undefined(self.value, compressor)
+            && is_undefined(value, compressor)
             && !in_async_generator(compressor.find_parent(AST_Scope))) {
             self.value = null;
         }
index 34479d4..577474b 100644 (file)
@@ -182,6 +182,34 @@ dont_inline: {
     node_version: ">=8"
 }
 
+dont_inline_nested: {
+    options = {
+        inline: true,
+    }
+    input: {
+        function await() {
+            return "PASS";
+        }
+        (async function() {
+            (function() {
+                console.log(await("FAIL"));
+            })();
+        })();
+    }
+    expect: {
+        function await() {
+            return "PASS";
+        }
+        (async function() {
+            (function() {
+                console.log(await("FAIL"));
+            })();
+        })();
+    }
+    expect_stdout: "PASS"
+    node_version: ">=8"
+}
+
 inline_await_1: {
     options = {
         awaits: true,
index 1b1aea1..0b4f622 100644 (file)
@@ -1310,6 +1310,7 @@ issue_4725_1: {
 
 issue_4725_2: {
     options = {
+        if_return: true,
         inline: true,
     }
     input: {
index a5b51dc..c048559 100644 (file)
@@ -3731,6 +3731,7 @@ issue_2437_1: {
     options = {
         collapse_vars: true,
         conditionals: true,
+        if_return: true,
         inline: true,
         join_vars: true,
         passes: 2,
@@ -3783,6 +3784,7 @@ issue_2437_2: {
         conditionals: true,
         inline: true,
         join_vars: true,
+        negate_iife: true,
         passes: 3,
         reduce_funcs: true,
         reduce_vars: true,
@@ -9374,9 +9376,8 @@ inline_throw: {
     expect: {
         try {
             (function(a) {
-                return function() {
-                    throw a;
-                }();
+                throw a;
+                return;
             })("PASS");
         } catch (e) {
             console.log(e);
index 521a14d..83da5a4 100644 (file)
@@ -1219,9 +1219,9 @@ issue_4248: {
     expect_stdout: "PASS"
 }
 
-issue_4261: {
+issue_4261_1: {
     options = {
-        inline: true,
+        inline: 3,
         reduce_funcs: true,
         reduce_vars: true,
         toplevel: true,
@@ -1259,6 +1259,45 @@ issue_4261: {
     expect_stdout: "42"
 }
 
+issue_4261_2: {
+    options = {
+        if_return: true,
+        inline: true,
+        reduce_funcs: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        {
+            const a = 42;
+            (function() {
+                function f() {
+                    console.log(a);
+                }
+                function g() {
+                    while (f());
+                }
+                (function() {
+                    while (g());
+                })();
+            })();
+        }
+    }
+    expect: {
+        {
+            const a = 42;
+            (function() {
+                function g() {
+                    while (void console.log(a));
+                }
+                while (g());
+            })();
+        }
+    }
+    expect_stdout: "42"
+}
+
 issue_4274_1: {
     options = {
         loops: true,
index 5fc6981..b888672 100644 (file)
@@ -1866,7 +1866,7 @@ issue_5057_2: {
 
 issue_5057_3: {
     options = {
-        inline: true,
+        inline: 3,
         unused: true,
     }
     input: {
@@ -1889,6 +1889,31 @@ issue_5057_3: {
     node_version: ">=6"
 }
 
+issue_5057_4: {
+    options = {
+        if_return: true,
+        inline: true,
+        unused: true,
+    }
+    input: {
+        (function(a) {
+            (function f(b) {
+                (function(a = console.log("FAIL 1")) {})(b);
+                console.log(a);
+            })("FAIL 2");
+        })("PASS");
+    }
+    expect: {
+        (function(a) {
+            var b = "FAIL 2";
+            (function(a = console.log("FAIL 1")) {})(b);
+            console.log(a);
+        })("PASS");
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
+
 issue_5065: {
     options = {
         pure_getters: "strict",
index 5acc7f6..a13d920 100644 (file)
@@ -2069,7 +2069,7 @@ issue_4319: {
 
 issue_4321: {
     options = {
-        inline: true,
+        inline: 3,
         keep_fargs: false,
     }
     input: {
index 2841f9e..dff92d0 100644 (file)
@@ -679,6 +679,26 @@ inline_loop_4: {
     }
 }
 
+inline_negate_iife: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function() {
+            return !function() {
+                while (!console);
+            }();
+        }());
+    }
+    expect: {
+        console.log(function() {
+            while (!console);
+            return !void 0;
+        }());
+    }
+    expect_stdout: "true"
+}
+
 issue_2476: {
     options = {
         inline: true,
@@ -1029,7 +1049,7 @@ issue_2616: {
 
 issue_2620_1: {
     options = {
-        inline: true,
+        inline: 3,
         reduce_vars: true,
         sequences: true,
         side_effects: true,
@@ -1063,6 +1083,42 @@ issue_2620_1: {
 }
 
 issue_2620_2: {
+    options = {
+        inline: true,
+        reduce_vars: true,
+        sequences: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        var c = "FAIL";
+        (function() {
+            function f(a) {
+                var b = function g(a) {
+                    a && a();
+                }();
+                if (a) {
+                    var d = c = "PASS";
+                }
+            }
+            f(1);
+        })();
+        console.log(c);
+    }
+    expect: {
+        var c = "FAIL";
+        (function() {
+            var a = 1;
+            if (function(a) {
+                a && a();
+            }(), a) c = "PASS";
+        })(),
+        console.log(c);
+    }
+    expect_stdout: "PASS"
+}
+
+issue_2620_3: {
     options = {
         conditionals: true,
         evaluate: true,
@@ -1096,10 +1152,10 @@ issue_2620_2: {
     expect_stdout: "PASS"
 }
 
-issue_2620_3: {
+issue_2620_4: {
     options = {
         evaluate: true,
-        inline: true,
+        inline: 3,
         reduce_vars: true,
         side_effects: true,
         unused: true,
@@ -1139,7 +1195,50 @@ issue_2620_3: {
     expect_stdout: "PASS"
 }
 
-issue_2620_4: {
+issue_2620_5: {
+    options = {
+        evaluate: true,
+        inline: true,
+        reduce_vars: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        var c = "FAIL";
+        (function() {
+            function f(a, NaN) {
+                function g() {
+                    switch (a) {
+                      case a:
+                        break;
+                      case c = "PASS", NaN:
+                        break;
+                    }
+                }
+                g();
+            }
+            f(0/0);
+        })();
+        console.log(c);
+    }
+    expect: {
+        var c = "FAIL";
+        (function() {
+            var a = 0/0;
+            var NaN = void 0;
+            switch (a) {
+              case a:
+                break;
+              case c = "PASS", NaN:
+                break;
+            }
+        })();
+        console.log(c);
+    }
+    expect_stdout: "PASS"
+}
+
+issue_2620_6: {
     rename = true
     options = {
         dead_code: true,
@@ -1636,6 +1735,28 @@ duplicate_argnames_3: {
     expect_stdout: "PASS"
 }
 
+duplicate_argnames_4: {
+    options = {
+        if_return: true,
+        inline: true,
+    }
+    input: {
+        (function() {
+            (function(a, a) {
+                while (console.log(a || "PASS"));
+            })("FAIL");
+        })();
+    }
+    expect: {
+        (function() {
+            var a = "FAIL";
+            var a = void 0;
+            while (console.log(a || "PASS"));
+        })();
+    }
+    expect_stdout: "PASS"
+}
+
 loop_init_arg: {
     options = {
         inline: true,
@@ -2885,6 +3006,7 @@ issue_2437: {
         collapse_vars: true,
         conditionals: true,
         functions: true,
+        if_return: true,
         inline: true,
         join_vars: true,
         passes: 2,
@@ -3310,7 +3432,28 @@ issue_3402: {
     ]
 }
 
-issue_3439: {
+issue_3439_1: {
+    options = {
+        inline: 3,
+    }
+    input: {
+        console.log(typeof function() {
+            return function(a) {
+                function a() {}
+                return a;
+            }(42);
+        }());
+    }
+    expect: {
+        console.log(typeof function(a) {
+            function a() {}
+            return a;
+        }(42));
+    }
+    expect_stdout: "function"
+}
+
+issue_3439_2: {
     options = {
         inline: true,
     }
@@ -3390,7 +3533,7 @@ issue_3506_2: {
     options = {
         collapse_vars: true,
         evaluate: true,
-        inline: true,
+        inline: 3,
         reduce_vars: true,
         side_effects: true,
         unused: true,
@@ -3419,9 +3562,40 @@ issue_3506_2: {
 issue_3506_3: {
     options = {
         collapse_vars: true,
-        dead_code: true,
         evaluate: true,
         inline: true,
+        reduce_vars: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        var a = "FAIL";
+        (function(b) {
+            (function(c) {
+                var d = 1;
+                for (;c && (a = "PASS") && 0 < --d;);
+            })(b);
+        })(a);
+        console.log(a);
+    }
+    expect: {
+        var a = "FAIL";
+        (function(b) {
+            var c = a;
+            var d = 1;
+            for (;c && (a = "PASS") && 0 < --d;);
+        })();
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}
+
+issue_3506_4: {
+    options = {
+        collapse_vars: true,
+        dead_code: true,
+        evaluate: true,
+        inline: 3,
         loops: true,
         reduce_vars: true,
         side_effects: true,
@@ -3448,6 +3622,39 @@ issue_3506_3: {
     expect_stdout: "PASS"
 }
 
+issue_3506_5: {
+    options = {
+        collapse_vars: true,
+        dead_code: true,
+        evaluate: true,
+        inline: true,
+        loops: true,
+        reduce_vars: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        var a = "FAIL";
+        (function(b) {
+            (function(c) {
+                var d = 1;
+                for (;c && (a = "PASS") && 0 < --d;);
+            })(b);
+        })(a);
+        console.log(a);
+    }
+    expect: {
+        var a = "FAIL";
+        (function(b) {
+            var c = a;
+            var d = 1;
+            for (;c && (a = "PASS") && 0 < --d;);
+        })();
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}
+
 issue_3512: {
     options = {
         collapse_vars: true,
@@ -3595,6 +3802,45 @@ hoisted_single_use: {
     ]
 }
 
+inlined_single_use: {
+    options = {
+        inline: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        console.log(function(f) {
+            f();
+        }(function() {
+            var a = function() {
+                A;
+            };
+            var b = function() {
+                a(B);
+            };
+            (function() {
+                b;
+            });
+            var c = 42;
+        }));
+    }
+    expect: {
+        console.log(function(f) {
+            var a = function() {
+                A;
+            };
+            var b = function() {
+                a(B);
+            };
+            (function() {
+                b;
+            });
+            return;
+        }());
+    }
+    expect_stdout: "undefined"
+}
+
 pr_3592_1: {
     options = {
         inline: true,
@@ -4286,9 +4532,9 @@ substitute: {
     ]
 }
 
-substitute_add_farg: {
+substitute_add_farg_1: {
     options = {
-        inline: true,
+        inline: 3,
         keep_fargs: false,
     }
     input: {
@@ -4323,6 +4569,46 @@ substitute_add_farg: {
     ]
 }
 
+substitute_add_farg_2: {
+    options = {
+        if_return: true,
+        inline: true,
+        keep_fargs: false,
+        side_effects: true,
+    }
+    input: {
+        function f(g) {
+            console.log(g.length);
+            g(null, "FAIL");
+        }
+        f(function() {
+            return function(a, b) {
+                return function(c) {
+                    do {
+                        console.log("PASS");
+                    } while (c);
+                }(a, b);
+            };
+        }());
+    }
+    expect: {
+        function f(g) {
+            console.log(g.length);
+            g(null, "FAIL");
+        }
+        f(function(a, b) {
+            var c = a;
+            do {
+                console.log("PASS");
+            } while (c);
+        });
+    }
+    expect_stdout: [
+        "2",
+        "PASS",
+    ]
+}
+
 substitute_arguments: {
     options = {
         inline: true,
@@ -4652,9 +4938,9 @@ substitute_use_strict: {
     ]
 }
 
-issue_3833: {
+issue_3833_1: {
     options = {
-        inline: true,
+        inline: 3,
         keep_fargs: false,
         reduce_vars: true,
         toplevel: true,
@@ -4679,6 +4965,33 @@ issue_3833: {
     expect_stdout: "PASS"
 }
 
+issue_3833_2: {
+    options = {
+        if_return: true,
+        inline: true,
+        keep_fargs: false,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        function f(a) {
+            return function() {
+                while (a);
+                console.log("PASS");
+            }();
+        }
+        f();
+    }
+    expect: {
+        (function(a) {
+            while (a);
+            console.log("PASS");
+        })();
+    }
+    expect_stdout: "PASS"
+}
+
 issue_3835: {
     options = {
         inline: true,
@@ -4699,9 +5012,9 @@ issue_3835: {
     expect_stdout: true
 }
 
-issue_3836: {
+issue_3836_1: {
     options = {
-        inline: true,
+        inline: 3,
     }
     input: {
         (function() {
@@ -4720,6 +5033,29 @@ issue_3836: {
     expect_stdout: "PASS"
 }
 
+issue_3836_2: {
+    options = {
+        if_return: true,
+        inline: true,
+    }
+    input: {
+        (function() {
+            return function() {
+                for (var a in 0)
+                    console.log(k);
+            }(console.log("PASS"));
+        })();
+    }
+    expect: {
+        (function() {
+            console.log("PASS");
+            for (var a in 0)
+                console.log(k);
+        })();
+    }
+    expect_stdout: "PASS"
+}
+
 issue_3852: {
     options = {
         collapse_vars: true,
@@ -5208,6 +5544,7 @@ issue_4259: {
 
 issue_4261: {
     options = {
+        if_return: true,
         inline: true,
         reduce_funcs: true,
         reduce_vars: true,
@@ -5237,15 +5574,15 @@ issue_4261: {
         } catch (e) {
             (function() {
                 function g() {
+                    // `ReferenceError: e is not defined` on Node.js v0.10
                     while (void e.p);
                 }
-                (function() {
-                    while (console.log(g()));
-                })();
+                while (console.log(g()));
             })();
         }
     }
-    expect_stdout: true
+    expect_stdout: "undefined"
+    node_version: "<0.10 || >=0.12"
 }
 
 issue_4265: {
@@ -5500,6 +5837,7 @@ issue_4655: {
 
 issue_4659_1: {
     options = {
+        if_return: true,
         inline: true,
         reduce_vars: true,
     }
@@ -5524,11 +5862,9 @@ issue_4659_1: {
             function f() {
                 return a++;
             }
+            f && a++;
             (function() {
-                f && a++;
-                (function() {
-                    var a = console && a;
-                })();
+                var a = console && a;
             })();
         })();
         console.log(a);
@@ -5538,6 +5874,7 @@ issue_4659_1: {
 
 issue_4659_2: {
     options = {
+        if_return: true,
         inline: true,
         reduce_vars: true,
     }
@@ -5564,11 +5901,9 @@ issue_4659_2: {
             function f() {
                 return a++;
             }
+            void (f && a++);
             (function() {
-                void (f && a++);
-                (function() {
-                    var a = console && a;
-                })();
+                var a = console && a;
             })();
         })();
         console.log(a);
@@ -5578,6 +5913,7 @@ issue_4659_2: {
 
 issue_4659_3: {
     options = {
+        if_return: true,
         inline: true,
         reduce_vars: true,
         unused: true,
@@ -5607,12 +5943,10 @@ issue_4659_3: {
                 return a++;
             }
             (function() {
-                (function() {
-                    while (!console);
-                })(f && a++);
-                (function() {
-                    var a = console && a;
-                })();
+                while (!console);
+            })(f && a++);
+            (function() {
+                var a = console && a;
             })();
         })();
         console.log(a);
@@ -5787,6 +6121,7 @@ issue_4725_1: {
 
 issue_4725_2: {
     options = {
+        if_return: true,
         inline: true,
     }
     input: {
index 28e6a57..e19ca6f 100644 (file)
@@ -545,10 +545,10 @@ if_body_return_3: {
     ]
 }
 
-issue_3600: {
+issue_3600_1: {
     options = {
         if_return: true,
-        inline: true,
+        inline: 3,
         side_effects: true,
         unused: true,
     }
@@ -574,6 +574,37 @@ issue_3600: {
     expect_stdout: "1"
 }
 
+issue_3600_2: {
+    options = {
+        if_return: true,
+        inline: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        var c = 0;
+        (function() {
+            if ([ ][c++]); else return;
+            return void function() {
+                var b = --b, a = c = 42;
+                return c;
+            }();
+        })();
+        console.log(c);
+    }
+    expect: {
+        var c = 0;
+        (function() {
+            if ([][c++]) {
+                var b = --b;
+                c = 42;
+            }
+        })();
+        console.log(c);
+    }
+    expect_stdout: "1"
+}
+
 iife_if_return_simple: {
     options = {
         conditionals: true,
index b69a088..2c7bf27 100644 (file)
@@ -1217,6 +1217,7 @@ issues_3267_1: {
         evaluate: true,
         inline: true,
         keep_fargs: false,
+        negate_iife: true,
         reduce_vars: true,
         sequences: true,
         side_effects: true,
index f98f88e..7a150af 100644 (file)
@@ -6671,6 +6671,7 @@ issues_3267_1: {
         dead_code: true,
         evaluate: true,
         inline: true,
+        negate_iife: true,
         reduce_vars: true,
         sequences: true,
         side_effects: true,
@@ -6688,7 +6689,7 @@ issues_3267_1: {
         });
     }
     expect: {
-        !function(i) {
+        !function(x) {
             if (Object())
                 return console.log("PASS");
             throw "FAIL";
@@ -6705,6 +6706,7 @@ issues_3267_2: {
         evaluate: true,
         inline: true,
         keep_fargs: false,
+        negate_iife: true,
         passes: 2,
         reduce_vars: true,
         sequences: true,
index 8c34a0d..dc77f2e 100644 (file)
@@ -147,7 +147,7 @@ dont_inline: {
     node_version: ">=6"
 }
 
-do_inline: {
+do_inline_1: {
     options = {
         inline: true,
         spreads: true,
@@ -164,6 +164,48 @@ do_inline: {
     node_version: ">=6"
 }
 
+do_inline_2: {
+    options = {
+        inline: true,
+        side_effects: true,
+    }
+    input: {
+        (function() {
+            (function() {
+                console.log("PASS");
+            })(..."");
+        })();
+    }
+    expect: {
+        [] = [ ..."" ],
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
+
+do_inline_3: {
+    options = {
+        if_return: true,
+        inline: true,
+    }
+    input: {
+        (function() {
+            (function() {
+                while (console.log("PASS"));
+            })(..."");
+        })();
+    }
+    expect: {
+        (function() {
+            var [] = [ ..."" ];
+            while (console.log("PASS"));
+        })();
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
+
 drop_empty_call_1: {
     options = {
         side_effects: true,
index f0ffded..306ec6e 100644 (file)
@@ -784,6 +784,30 @@ inline_nested_yield: {
     node_version: ">=4"
 }
 
+dont_inline_nested: {
+    options = {
+        inline: true,
+    }
+    input: {
+        var yield = "PASS";
+        (function*() {
+            (function() {
+                console.log(yield);
+            })();
+        })().next();
+    }
+    expect: {
+        var yield = "PASS";
+        (function*() {
+            (function() {
+                console.log(yield);
+            })();
+        })().next();
+    }
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}
+
 drop_body: {
     options = {
         side_effects: true,