enhance `inline` & `unused` (#5245)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sat, 1 Jan 2022 21:40:43 +0000 (21:40 +0000)
committerGitHub <noreply@github.com>
Sat, 1 Jan 2022 21:40:43 +0000 (05:40 +0800)
17 files changed:
lib/compress.js
test/compress/classes.js
test/compress/const.js
test/compress/default-values.js
test/compress/functions.js
test/compress/issue-1704.js
test/compress/issue-892.js
test/compress/join_vars.js
test/compress/keep_fargs.js
test/compress/let.js
test/compress/nullish.js
test/compress/pure_getters.js
test/compress/reduce_vars.js
test/compress/sequences.js
test/compress/spreads.js
test/mocha/cli.js
test/mocha/spidermonkey.js

index ab930ff..8e93273 100644 (file)
@@ -1859,7 +1859,7 @@ Compressor.prototype.compress = function(node) {
                 if (!changed && last_changed == 3) break;
             }
             if (compressor.option("inline") >= 4) {
-                if (inline_last_iife(statements, compressor)) changed = 4;
+                if (inline_iife(statements, compressor)) changed = 4;
                 if (!changed && last_changed == 4) break;
             }
             if (compressor.sequences_limit > 0) {
@@ -3478,23 +3478,23 @@ Compressor.prototype.compress = function(node) {
             return statements.length != len;
         }
 
-        function inline_last_iife(statements, compressor) {
-            if (!in_lambda) return false;
+        function inline_iife(statements, compressor) {
+            var changed = 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;
+            if (in_lambda && index >= 0) {
+                var inlined = statements[index].try_inline(compressor, block_scope);
+                if (inlined) {
+                    statements[index--] = inlined;
+                    changed = true;
+                }
             }
-            var inlined = make_node(AST_UnaryPrefix, stat, {
-                operator: "void",
-                expression: body,
-            }).try_inline(compressor, block_scope);
-            if (!inlined) return false;
-            statements[index] = inlined;
-            return true;
+            for (; index >= 0; index--) {
+                var inlined = statements[index].try_inline(compressor, block_scope, true, in_loop);
+                if (!inlined) continue;
+                statements[index] = inlined;
+                changed = true;
+            }
+            return changed;
         }
 
         function sequencesize(statements, compressor) {
@@ -3803,6 +3803,7 @@ Compressor.prototype.compress = function(node) {
                         if (stat.init) {
                             prev.definitions = prev.definitions.concat(stat.init.definitions);
                         }
+                        stat = stat.clone();
                         defs = stat.init = prev;
                         statements[j] = merge_defns(stat);
                         changed = true;
@@ -6775,7 +6776,7 @@ Compressor.prototype.compress = function(node) {
                     var sym = def.name.definition();
                     var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym);
                     if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
-                        if (value && indexOf_assign(sym, def) < 0) {
+                        if (value && (indexOf_assign(sym, def) < 0 || self_assign(value.tail_node()))) {
                             value = value.drop_side_effect_free(compressor);
                             if (value) {
                                 AST_Node.warn("Side effects in definition of variable {name} [{file}:{line},{col}]", template(def.name));
@@ -6865,6 +6866,10 @@ Compressor.prototype.compress = function(node) {
                         sym.eliminated++;
                     }
 
+                    function self_assign(ref) {
+                        return ref instanceof AST_SymbolRef && ref.definition() === sym;
+                    }
+
                     function assigned_once(fn, refs) {
                         if (refs.length == 0) return fn === def.name.fixed_value();
                         return all(refs, function(ref) {
@@ -9832,15 +9837,15 @@ Compressor.prototype.compress = function(node) {
                 if (!has_default) has_default = 1;
                 var arg = has_default == 1 && self.args[index];
                 if (arg && !is_undefined(arg)) has_default = 2;
-                if (has_arg_refs(argname.value)) return false;
+                if (has_arg_refs(fn, argname.value)) return false;
                 argname = argname.name;
             }
             if (argname instanceof AST_Destructured) {
                 has_destructured = true;
-                if (has_arg_refs(argname)) return false;
+                if (has_arg_refs(fn, argname)) return false;
             }
             return true;
-        }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
+        }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn, fn.rest));
         var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
         if (can_inline && stat instanceof AST_Return) {
             var value = stat.value;
@@ -9946,17 +9951,6 @@ Compressor.prototype.compress = function(node) {
         }
         return try_evaluate(compressor, self);
 
-        function has_arg_refs(node) {
-            var found = false;
-            node.walk(new TreeWalker(function(node) {
-                if (found) return true;
-                if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
-                    return found = true;
-                }
-            }));
-            return found;
-        }
-
         function make_void_lhs(orig) {
             return make_node(AST_Dot, orig, {
                 expression: make_node(AST_Array, orig, { elements: [] }),
@@ -10172,7 +10166,7 @@ Compressor.prototype.compress = function(node) {
                     if (arg_used.has(name.name)) return false;
                     if (var_exists(defined, name.name)) return false;
                     if (!all(stat.enclosed, function(def) {
-                        return def.scope === stat || !defined.has(def.name);
+                        return def.scope === scope || def.scope === stat || !defined.has(def.name);
                     })) return false;
                     if (in_loop) in_loop.push(name.definition());
                     continue;
@@ -10207,8 +10201,6 @@ Compressor.prototype.compress = function(node) {
                     if (scope.init === child) continue;
                     if (scope.object === child) continue;
                     in_loop = [];
-                } else if (scope instanceof AST_SymbolRef) {
-                    if (scope.fixed_value() instanceof AST_Scope) return false;
                 }
             } while (!(scope instanceof AST_Scope));
             insert = scope.body.indexOf(child) + 1;
@@ -10270,7 +10262,9 @@ Compressor.prototype.compress = function(node) {
                     if (value) expressions.push(value);
                 } else {
                     var symbol = make_node(AST_SymbolVar, name, name);
-                    name.definition().orig.push(symbol);
+                    var def = name.definition();
+                    def.orig.push(symbol);
+                    def.eliminated++;
                     if (name.unused !== undefined) {
                         append_var(decls, expressions, symbol);
                         if (value) expressions.push(value);
@@ -10316,44 +10310,36 @@ Compressor.prototype.compress = function(node) {
                 def.references.push(ref);
                 var symbol = make_node(AST_SymbolVar, name, name);
                 def.orig.push(symbol);
+                def.eliminated++;
                 append_var(decls, expressions, symbol);
             }
         }
 
-        function flatten_var(name) {
-            var redef = name.definition().redefined();
-            if (redef) {
-                name = name.clone();
-                name.thedef = redef;
-            }
-            return name;
-        }
-
         function flatten_vars(decls, expressions) {
             var args = [ insert, 0 ];
             var decl_var = [], expr_var = [], expr_loop = [], exprs = [];
-            for (var i = 0; i < fn.body.length; i++) {
-                var stat = fn.body[i];
-                if (stat instanceof AST_LambdaDefinition) {
-                    if (in_loop) {
-                        var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
-                        var def = name.definition();
-                        def.fixed = false;
-                        def.orig.push(name);
-                        append_var(decls, expressions, name, to_func_expr(stat, true));
-                    } else {
-                        var def = stat.name.definition();
-                        scope.functions.set(def.name, def);
-                        scope.variables.set(def.name, def);
-                        scope.enclosed.push(def);
-                        scope.var_names().set(def.name, true);
-                        args.push(stat);
-                    }
-                    continue;
-                }
+            fn.body.filter(in_loop ? function(stat) {
+                if (!(stat instanceof AST_LambdaDefinition)) return true;
+                var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
+                var def = name.definition();
+                def.fixed = false;
+                def.orig.push(name);
+                def.eliminated++;
+                append_var(decls, expressions, name, to_func_expr(stat, true));
+                return false;
+            } : function(stat) {
+                if (!(stat instanceof AST_LambdaDefinition)) return true;
+                var def = stat.name.definition();
+                scope.functions.set(def.name, def);
+                scope.variables.set(def.name, def);
+                scope.enclosed.push(def);
+                scope.var_names().set(def.name, true);
+                args.push(stat);
+                return false;
+            }).forEach(function(stat) {
                 if (!(stat instanceof AST_Var)) {
                     if (stat instanceof AST_SimpleStatement) exprs.push(stat.body);
-                    continue;
+                    return;
                 }
                 for (var j = 0; j < stat.definitions.length; j++) {
                     var var_def = stat.definitions[j];
@@ -10365,7 +10351,7 @@ Compressor.prototype.compress = function(node) {
                         exprs = [];
                     }
                     append_var(decl_var, expr_var, name, value);
-                    if (in_loop && !arg_used.has(name.name)) {
+                    if (in_loop && !arg_used.has(name.name) && !fn.functions.has(name.name)) {
                         var def = fn.variables.get(name.name);
                         var sym = make_node(AST_SymbolRef, name, name);
                         def.references.push(sym);
@@ -10376,7 +10362,7 @@ Compressor.prototype.compress = function(node) {
                         }));
                     }
                 }
-            }
+            });
             [].push.apply(decls, decl_var);
             [].push.apply(expressions, expr_loop);
             [].push.apply(expressions, expr_var);
@@ -10393,9 +10379,7 @@ Compressor.prototype.compress = function(node) {
             }
             var args = flatten_vars(decls, expressions);
             expressions.push(value);
-            if (decls.length) args.push(make_node(AST_Var, fn, {
-                definitions: decls
-            }));
+            if (decls.length) args.push(make_node(AST_Var, fn, { definitions: decls }));
             [].splice.apply(scope.body, args);
             fn.enclosed.forEach(function(def) {
                 if (scope.var_names().has(def.name)) return;
@@ -11809,9 +11793,7 @@ Compressor.prototype.compress = function(node) {
         var reachable = false;
         var find_ref = new TreeWalker(function(node) {
             if (reachable) return true;
-            if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
-                return reachable = true;
-            }
+            if (node instanceof AST_SymbolRef && member(node.definition(), defs)) return reachable = true;
         });
         var scan_scope = new TreeWalker(function(node) {
             if (reachable) return true;
@@ -12825,12 +12807,72 @@ Compressor.prototype.compress = function(node) {
         }
     });
 
+    function flatten_var(name) {
+        var redef = name.definition().redefined();
+        if (redef) {
+            name = name.clone();
+            name.thedef = redef;
+        }
+        return name;
+    }
+
+    function has_arg_refs(fn, node) {
+        var found = false;
+        node.walk(new TreeWalker(function(node) {
+            if (found) return true;
+            if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
+                return found = true;
+            }
+        }));
+        return found;
+    }
+
     (function(def) {
         def(AST_Node, noop);
-        def(AST_Await, function(compressor, scope) {
-            return this.expression.try_inline(compressor, scope);
+        def(AST_Assign, noop);
+        def(AST_Await, function(compressor, scope, no_return, in_loop) {
+            return this.expression.try_inline(compressor, scope, no_return, in_loop);
         });
-        def(AST_Call, function(compressor, scope) {
+        def(AST_Binary, function(compressor, scope, no_return, in_loop) {
+            if (no_return === undefined) return;
+            var self = this;
+            var op = self.operator;
+            if (!lazy_op[op]) return;
+            var inlined = self.right.try_inline(compressor, scope, no_return, in_loop);
+            if (!inlined) return;
+            return make_node(AST_If, self, {
+                condition: make_condition(self.left),
+                body: inlined,
+                alternative: null,
+            });
+
+            function make_condition(cond) {
+                switch (op) {
+                  case "&&":
+                    return cond;
+                  case "||":
+                    return cond.negate(compressor);
+                  case "??":
+                    return make_node(AST_Binary, self, {
+                        operator: "===",
+                        left: make_node(AST_Undefined, self).transform(compressor),
+                        right: cond,
+                    });
+                }
+            }
+        });
+        def(AST_BlockStatement, function(compressor, scope, no_return, in_loop) {
+            if (no_return) return;
+            if (!this.variables) return;
+            var body = this.body;
+            var last = body.length - 1;
+            if (last < 0) return;
+            var inlined = body[last].try_inline(compressor, this, no_return, in_loop);
+            if (!inlined) return;
+            body[last] = inlined;
+            return this;
+        });
+        def(AST_Call, function(compressor, scope, no_return, in_loop) {
             if (compressor.option("inline") < 4) return;
             var call = this;
             if (call.is_expr_pure(compressor)) return;
@@ -12838,8 +12880,10 @@ Compressor.prototype.compress = function(node) {
             if (!(fn instanceof AST_LambdaExpression)) return;
             if (fn.name) return;
             if (fn.uses_arguments) return;
-            if (is_arrow(fn) && fn.value) return;
             if (is_generator(fn)) return;
+            var arrow = is_arrow(fn);
+            if (arrow && fn.value) return;
+            if (fn.body[0] instanceof AST_Directive) return;
             if (fn.contains_this()) return;
             if (!scope) scope = find_scope(compressor);
             var defined = new Dictionary();
@@ -12849,28 +12893,82 @@ Compressor.prototype.compress = function(node) {
                 });
                 scope = scope.parent_scope;
             }
-            if (is_async(fn) && !(compressor.option("awaits") && is_async(scope))) return;
+            if (scope instanceof AST_Toplevel && fn.variables.size() > (arrow ? 0 : 1)) {
+                if (!compressor.toplevel.vars) return;
+                if (fn.functions.size() > 0 && !compressor.toplevel.funcs) return;
+            }
+            var async = is_async(fn);
+            if (async && !(compressor.option("awaits") && is_async(scope))) return;
             var names = scope.var_names();
+            if (in_loop) in_loop = [];
             if (!fn.variables.all(function(def, name) {
+                if (in_loop) in_loop.push(def);
                 if (!defined.has(name) && !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 (in_loop && in_loop.length > 0 && is_reachable(fn, in_loop)) return;
+            var simple_argnames = true;
+            if (!all(fn.argnames, function(argname) {
+                var abort = false;
+                var tw = new TreeWalker(function(node) {
+                    if (abort) return true;
+                    if (node instanceof AST_DefaultValue) {
+                        if (has_arg_refs(fn, node.value)) return abort = true;
+                        node.name.walk(tw);
+                        return true;
+                    }
+                    if (node instanceof AST_DestructuredKeyVal) {
+                        if (node.key instanceof AST_Node && has_arg_refs(fn, node.key)) return abort = true;
+                        node.value.walk(tw);
+                        return true;
+                    }
+                    if (node instanceof AST_SymbolFunarg && !all(node.definition().orig, function(sym) {
+                        return !(sym instanceof AST_SymbolDefun);
+                    })) return abort = true;
+                });
+                argname.walk(tw);
+                if (abort) return false;
+                if (!(argname instanceof AST_SymbolFunarg)) simple_argnames = false;
+                return true;
+            })) return;
+            if (fn.rest) {
+                if (has_arg_refs(fn, fn.rest)) return;
+                simple_argnames = false;
+            }
+            if (no_return && !all(fn.body, function(stat) {
+                var abort = false;
+                stat.walk(new TreeWalker(function(node) {
+                    if (abort) return true;
+                    if (async && node instanceof AST_Await || node instanceof AST_Return) return abort = true;
+                    if (node instanceof AST_Scope && node !== fn) return true;
+                }));
+                return !abort;
+            })) return;
             if (!safe_from_await_yield(fn, avoid_await_yield(scope))) return;
             fn.functions.each(function(def, name) {
                 scope.functions.set(name, def);
             });
+            var body = [];
             fn.variables.each(function(def, name) {
+                names.set(name, true);
+                scope.enclosed.push(def);
                 scope.variables.set(name, def);
                 def.single_use = false;
+                if (!in_loop || fn.functions.has(name)) return;
+                if (def.references.length == def.replaced) return;
+                var sym = flatten_var(def.orig[0]);
+                if (sym.TYPE != "SymbolVar") return;
+                var ref = make_node(AST_SymbolRef, sym, sym);
+                sym.definition().references.push(ref);
+                body.push(make_node(AST_SimpleStatement, sym, {
+                    body: make_node(AST_Assign, sym, {
+                        operator: "=",
+                        left: ref,
+                        right: make_node(AST_Undefined, sym).transform(compressor),
+                    }),
+                }));
             });
             if (fn.variables.has("NaN")) scope.transform(new TreeTransformer(function(node) {
                 if (node instanceof AST_NaN) return make_node(AST_Binary, node, {
@@ -12879,25 +12977,10 @@ Compressor.prototype.compress = function(node) {
                     right: make_node(AST_Number, node, { value: 0 }),
                 });
             }));
-            var body = [], defs = Object.create(null), syms = new Dictionary();
-            if (fn.rest || !all(fn.argnames, function(argname) {
-                return argname instanceof AST_SymbolFunarg;
-            }) || !all(call.args, function(arg) {
+            var defs = Object.create(null), syms = new Dictionary();
+            if (simple_argnames && 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();
@@ -12908,26 +12991,57 @@ Compressor.prototype.compress = function(node) {
                     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),
+                            value: value || make_node(AST_Undefined, call).transform(compressor),
                         }) ],
                     }));
                 });
                 if (values.length) body.push(make_node(AST_SimpleStatement, call, {
                     body: make_sequence(call, values),
                 }));
+            } else {
+                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() }),
+                    }) ],
+                }));
             }
             syms.each(function(orig, id) {
-                [].unshift.apply(defs[id].orig, orig);
-            });
-            var inlined = make_node(AST_BlockStatement, call, {
-                body: body.concat(fn.body, make_node(AST_Return, call, { value: null })),
-            });
-            if (is_async(fn)) scan_local_returns(inlined, function(node) {
-                var value = node.value;
-                if (!value) return;
-                if (is_undefined(value)) return;
-                node.value = make_node(AST_Await, call, { expression: value });
+                var def = defs[id];
+                [].unshift.apply(def.orig, orig);
+                def.eliminated += orig.length;
             });
+            [].push.apply(body, in_loop ? fn.body.filter(function(stat) {
+                if (!(stat instanceof AST_LambdaDefinition)) return true;
+                var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
+                var def = name.definition();
+                def.fixed = false;
+                def.orig.push(name);
+                def.eliminated++;
+                body.push(make_node(AST_Var, stat, {
+                    definitions: [ make_node(AST_VarDef, stat, {
+                        name: name,
+                        value: to_func_expr(stat, true),
+                    }) ],
+                }));
+                return false;
+            }) : fn.body);
+            var inlined = make_node(AST_BlockStatement, call, { body: body });
+            if (!no_return) {
+                if (async) scan_local_returns(inlined, function(node) {
+                    var value = node.value;
+                    if (!value) return;
+                    if (is_undefined(value)) return;
+                    node.value = make_node(AST_Await, call, { expression: value });
+                });
+                body.push(make_node(AST_Return, call, { value: null }));
+            }
             return inlined;
 
             function process(sym, argname) {
@@ -12936,25 +13050,129 @@ Compressor.prototype.compress = function(node) {
                 syms.add(def.id, sym);
             }
         });
+        def(AST_Conditional, function(compressor, scope, no_return, in_loop) {
+            var self = this;
+            var body = self.consequent.try_inline(compressor, scope, no_return, in_loop);
+            var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop);
+            if (!body && !alt) return;
+            return make_node(AST_If, self, {
+                condition: self.condition,
+                body: body || make_body(self.consequent),
+                alternative: alt || make_body(self.alternative),
+            });
+
+            function make_body(value) {
+                if (no_return) return make_node(AST_SimpleStatement, value, { body: value });
+                return make_node(AST_Return, value, { value: value });
+            }
+        });
+        def(AST_For, function(compressor, scope, no_return, in_loop) {
+            var body = this.body.try_inline(compressor, scope, true, true);
+            if (body) this.body = body;
+            var inlined = this.init;
+            if (inlined) {
+                inlined = inlined.try_inline(compressor, scope, true, in_loop);
+                if (inlined) {
+                    this.init = null;
+                    inlined.body.push(this);
+                    return inlined;
+                }
+            }
+            return body && this;
+        });
+        def(AST_ForEnumeration, function(compressor, scope, no_return, in_loop) {
+            var body = this.body.try_inline(compressor, scope, true, true);
+            if (body) this.body = body;
+            var obj = this.object;
+            if (obj instanceof AST_Sequence) {
+                var inlined = inline_sequence(compressor, scope, true, in_loop, obj, 1);
+                if (inlined) {
+                    this.object = obj.tail_node();
+                    inlined.body.push(this);
+                    return inlined;
+                }
+            }
+            return body && this;
+        });
+        def(AST_If, function(compressor, scope, no_return, in_loop) {
+            var body = this.body.try_inline(compressor, scope, no_return, in_loop);
+            if (body) this.body = body;
+            var alt = this.alternative;
+            if (alt) {
+                alt = alt.try_inline(compressor, scope, no_return, in_loop);
+                if (alt) this.alternative = alt;
+            }
+            var cond = this.condition;
+            if (cond instanceof AST_Sequence) {
+                var inlined = inline_sequence(compressor, scope, true, in_loop, cond, 1);
+                if (inlined) {
+                    this.condition = cond.tail_node();
+                    inlined.body.push(this);
+                    return inlined;
+                }
+            }
+            return (body || alt) && this;
+        });
+        def(AST_IterationStatement, function(compressor, scope, no_return, in_loop) {
+            var body = this.body.try_inline(compressor, scope, true, true);
+            if (!body) return;
+            this.body = body;
+            return this;
+        });
+        def(AST_LabeledStatement, function(compressor, scope, no_return, in_loop) {
+            var body = this.body.try_inline(compressor, scope, no_return, in_loop);
+            if (!body) return;
+            this.body = body;
+            return this;
+        });
         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,
-                ],
+        function inline_sequence(compressor, scope, no_return, in_loop, node, skip) {
+            var body = [], exprs = node.expressions, no_ret = no_return;
+            for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true) {
+                var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop);
+                if (!inlined) continue;
+                flush();
+                body.push(inlined);
+            }
+            if (body.length == 0) return;
+            flush();
+            if (!no_return && body[0] instanceof AST_SimpleStatement) {
+                body[0] = make_node(AST_Return, node, { value: body[0].body });
+            }
+            return make_node(AST_BlockStatement, node, { body: body.reverse() });
+
+            function flush() {
+                if (j > i + 1) body.push(make_node(AST_SimpleStatement, node, {
+                    body: make_sequence(node, exprs.slice(i + 1, j)),
+                }));
+                j = i;
+            }
+        }
+        def(AST_Sequence, function(compressor, scope, no_return, in_loop) {
+            return inline_sequence(compressor, scope, no_return, in_loop, this);
+        });
+        def(AST_SimpleStatement, function(compressor, scope, no_return, in_loop) {
+            var body = this.body;
+            while (body instanceof AST_UnaryPrefix) {
+                var op = body.operator;
+                if (unary_side_effects[op]) break;
+                if (op == "void") break;
+                body = body.expression;
+            }
+            if (!no_return && !is_undefined(body)) body = make_node(AST_UnaryPrefix, this, {
+                operator: "void",
+                expression: body,
             });
+            return body.try_inline(compressor, scope, no_return || false, in_loop);
         });
-        def(AST_UnaryPrefix, function(compressor, scope) {
+        def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop) {
             var self = this;
             var op = self.operator;
             if (unary_side_effects[op]) return;
-            var inlined = self.expression.try_inline(compressor, scope);
+            if (!no_return && op == "void") no_return = false;
+            var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop);
             if (!inlined) return;
-            scan_local_returns(inlined, function(node) {
+            if (!no_return) scan_local_returns(inlined, function(node) {
                 node.in_bool = false;
                 var value = node.value;
                 if (op == "void") {
@@ -12968,7 +13186,21 @@ Compressor.prototype.compress = function(node) {
             });
             return inlined;
         });
-        def(AST_Yield, function(compressor, scope) {
+        def(AST_With, function(compressor, scope, no_return, in_loop) {
+            var body = this.body.try_inline(compressor, scope, no_return, in_loop);
+            if (body) this.body = body;
+            var exp = this.expression;
+            if (exp instanceof AST_Sequence) {
+                var inlined = inline_sequence(compressor, scope, true, in_loop, exp, 1);
+                if (inlined) {
+                    this.expression = exp.tail_node();
+                    inlined.body.push(this);
+                    return inlined;
+                }
+            }
+            return body && this;
+        });
+        def(AST_Yield, function(compressor, scope, no_return, in_loop) {
             if (!compressor.option("yields")) return;
             if (!this.nested) return;
             var call = this.expression;
@@ -12986,7 +13218,7 @@ Compressor.prototype.compress = function(node) {
             }
             call = call.clone();
             call.expression = fn;
-            return call.try_inline(compressor, scope);
+            return call.try_inline(compressor, scope, no_return, in_loop);
         });
     })(function(node, func) {
         node.DEFMETHOD("try_inline", func);
index 0b4f622..bad695f 100644 (file)
@@ -1646,10 +1646,8 @@ issue_4962_1: {
         })(function g() {});
     }
     expect: {
-        (function g() {}),
-        void function() {
-            while (console.log(typeof g));
-        }();
+        (function g() {});
+        while (console.log(typeof g));
     }
     expect_stdout: "undefined"
     node_version: ">=12"
index 83da5a4..60c686c 100644 (file)
@@ -838,12 +838,10 @@ issue_4202: {
     expect: {
         {
             const o = {};
-            (function() {
-                function f() {
-                    o.p = 42;
-                }
-                f(f);
-            })();
+            function f() {
+                o.p = 42;
+            }
+            f(f);
             console.log(o.p++);
         }
     }
@@ -1287,12 +1285,10 @@ issue_4261_2: {
     expect: {
         {
             const a = 42;
-            (function() {
-                function g() {
-                    while (void console.log(a));
-                }
-                while (g());
-            })();
+            function g() {
+                while (void console.log(a));
+            }
+            while (g());
         }
     }
     expect_stdout: "42"
index 53ebb19..3ad55f0 100644 (file)
@@ -2027,13 +2027,11 @@ issue_5222: {
         f();
     }
     expect: {
-        (function() {
-            do {
-                a = void 0,
-                [ a ] = [];
-            } while (console.log("PASS"));
-            var a;
-        })();
+        do {
+            a = void 0,
+            [ a ] = [];
+        } while (console.log("PASS"));
+        var a;
     }
     expect_stdout: "PASS"
     node_version: ">=6"
index 002db1b..3cf4857 100644 (file)
@@ -604,6 +604,254 @@ empty_body: {
     }
 }
 
+inline_binary_and: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function() {
+            (function() {
+                while (console.log("foo"));
+                return "bar";
+            })() && (function() {
+                while (console.log("baz"));
+                return "moo";
+            })();
+        }());
+    }
+    expect: {
+        console.log(function() {
+            if (function() {
+                while (console.log("foo"));
+                return "bar";
+            }()) {
+                while (console.log("baz"));
+                return void "moo";
+                return;
+            }
+        }());
+    }
+    expect_stdout: [
+        "foo",
+        "baz",
+        "undefined",
+    ]
+}
+
+inline_binary_or: {
+    options = {
+        inline: true,
+    }
+    input: {
+        (function() {
+            while (console.log("foo"));
+        })() || (function() {
+            while (console.log("bar"));
+        })();
+    }
+    expect: {
+        if (!function() {
+            while (console.log("foo"));
+        }())
+            while (console.log("bar"));
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+}
+
+inline_conditional: {
+    options = {
+        inline: true,
+    }
+    input: {
+        (function() {
+            while (console.log("foo"));
+        })() ? (function() {
+            while (console.log("bar"));
+        })() : (function() {
+            while (console.log("baz"));
+        })();
+    }
+    expect: {
+        if (function() {
+            while (console.log("foo"));
+        }())
+            while (console.log("bar"));
+        else
+            while (console.log("baz"));
+    }
+    expect_stdout: [
+        "foo",
+        "baz",
+    ]
+}
+
+inline_do: {
+    options = {
+        inline: true,
+    }
+    input: {
+        do (function() {
+            while (console.log("foo"));
+        })();
+        while (function() {
+            while (console.log("bar"));
+        }());
+    }
+    expect: {
+        do {
+            while (console.log("foo"));
+        } while (function() {
+            while (console.log("bar"));
+        }());
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+}
+
+inline_finally_return: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function() {
+            try {
+                throw "FAIL";
+            } finally {
+                return function() {
+                    while (console.log("PASS"));
+                }(), 42;
+            }
+        }());
+    }
+    expect: {
+        console.log(function() {
+            try {
+                throw "FAIL";
+            } finally {
+                while (console.log("PASS"));
+                return 42;
+            }
+        }());
+    }
+    expect_stdout: [
+        "PASS",
+        "42",
+    ]
+}
+
+inline_for_init: {
+    options = {
+        inline: true,
+    }
+    input: {
+        for (function() {
+            while (console.log("foo"));
+        }(); function() {
+            while (console.log("bar"));
+        }(); function() {
+            while (console.log("baz"));
+        }()) (function() {
+            while (console.log("moo"));
+        })();
+    }
+    expect: {
+        while (console.log("foo"));
+        for (; function() {
+            while (console.log("bar"));
+        }(); function() {
+            while (console.log("baz"));
+        }()) {
+            while (console.log("moo"));
+        }
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+}
+
+inline_for_object: {
+    options = {
+        inline: true,
+    }
+    input: {
+        for (var a in function() {
+            while (console.log("foo"));
+        }(), function() {
+            while (console.log("bar"));
+        }()) (function() {
+            while (console.log("baz"));
+        })();
+    }
+    expect: {
+        while (console.log("foo"));
+        for (var a in function() {
+            while (console.log("bar"));
+        }()) {
+            while (console.log("baz"));
+        }
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+}
+
+inline_if_else: {
+    options = {
+        inline: true,
+    }
+    input: {
+        if (function() {
+            while (console.log("foo"));
+        }(), function() {
+            while (console.log("bar"));
+        }()) (function() {
+            while (console.log("baz"));
+        })();
+        else (function() {
+            while (console.log("moo"));
+        })();
+    }
+    expect: {
+        while (console.log("foo"));
+        if (function() {
+            while (console.log("bar"));
+        }()) {
+            while (console.log("baz"));
+        } else {
+            while (console.log("moo"));
+        }
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+        "moo",
+    ]
+}
+
+inline_label: {
+    options = {
+        inline: true,
+    }
+    input: {
+        L: (function() {
+            while (console.log("PASS"));
+        })()
+    }
+    expect: {
+        L: {
+            while (console.log("PASS"));
+        }
+    }
+    expect_stdout: "PASS"
+}
+
 inline_loop_1: {
     options = {
         inline: true,
@@ -679,6 +927,86 @@ inline_loop_4: {
     }
 }
 
+inline_loop_5: {
+    options = {
+        inline: true,
+        toplevel: true,
+    }
+    input: {
+        for (var a in "foo") {
+            (function() {
+                function f() {}
+                var f;
+                console.log(typeof f, a - f);
+            })();
+        }
+    }
+    expect: {
+        for (var a in "foo")
+            f = function() {},
+            void console.log(typeof f, a - f);
+        var f;
+    }
+    expect_stdout: [
+        "function NaN",
+        "function NaN",
+        "function NaN",
+    ]
+}
+
+inline_loop_6: {
+    options = {
+        inline: true,
+        toplevel: true,
+    }
+    input: {
+        for (var a in "foo") {
+            (function() {
+                var f;
+                function f() {}
+                console.log(typeof f, a - f);
+            })();
+        }
+    }
+    expect: {
+        for (var a in "foo")
+            f = function() {},
+            void console.log(typeof f, a - f);
+        var f;
+    }
+    expect_stdout: [
+        "function NaN",
+        "function NaN",
+        "function NaN",
+    ]
+}
+
+inline_loop_7: {
+    options = {
+        inline: true,
+        toplevel: true,
+    }
+    input: {
+        for (var a = 0; a < 2; a++) {
+            (function() {
+                var b = b && b[console.log("FAIL")] || "PASS";
+                while (console.log(b));
+            })();
+        }
+    }
+    expect: {
+        for (var a = 0; a < 2; a++) {
+            b = void 0;
+            var b = b && b[console.log("FAIL")] || "PASS";
+            while (console.log(b));
+        }
+    }
+    expect_stdout: [
+        "PASS",
+        "PASS",
+    ]
+}
+
 inline_negate_iife: {
     options = {
         inline: true,
@@ -699,6 +1027,111 @@ inline_negate_iife: {
     expect_stdout: "true"
 }
 
+inline_return_binary: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function() {
+            return function() {
+                while (console.log("foo"));
+                return "bar";
+            }() || function() {
+                while (console.log("baz"));
+                return "moo";
+            }();
+        }());
+    }
+    expect: {
+        console.log(function() {
+            while (console.log("foo"));
+            return "bar";
+        }() || function() {
+            while (console.log("baz"));
+            return "moo";
+        }());
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+}
+
+inline_return_conditional: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function() {
+            return console ? "foo" : function() {
+                while (console.log("bar"));
+                return "baz";
+            }();
+        }());
+    }
+    expect: {
+        console.log(function() {
+            if (console)
+                return "foo";
+            else {
+                while (console.log("bar"));
+                return "baz";
+                return;
+            }
+        }());
+    }
+    expect_stdout: "foo"
+}
+
+inline_while: {
+    options = {
+        inline: true,
+    }
+    input: {
+        while (function() {
+            while (console.log("foo"));
+        }()) (function() {
+            while (console.log("bar"));
+        })();
+    }
+    expect: {
+        while (function() {
+            while (console.log("foo"));
+        }()) {
+            while (console.log("bar"));
+        }
+    }
+    expect_stdout: "foo"
+}
+
+inline_with: {
+    options = {
+        inline: true,
+    }
+    input: {
+        with (+function() {
+            while (console.log("foo"));
+        }(), -function() {
+            while (console.log("bar"));
+        }()) ~function() {
+            while (console.log("baz"));
+        }();
+    }
+    expect: {
+        while (console.log("foo"));
+        with (-function() {
+            while (console.log("bar"));
+        }()) {
+            while (console.log("baz"));
+        }
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+        "baz",
+    ]
+}
+
 issue_2476: {
     options = {
         inline: true,
@@ -820,16 +1253,14 @@ issue_2604_1: {
     }
     expect: {
         var a = "FAIL";
-        (function() {
-            try {
-                throw 1;
-            } catch (b) {
-                (function(b) {
-                    b && b();
-                })();
-                b && (a = "PASS");
-            }
-        })();
+        try {
+            throw 1;
+        } catch (b) {
+            (function(b) {
+                b && b();
+            })();
+            b && (a = "PASS");
+        }
         console.log(a);
     }
     expect_stdout: "PASS"
@@ -862,13 +1293,11 @@ issue_2604_2: {
     }
     expect: {
         var a = "FAIL";
-        (function() {
-            try {
-                throw 1;
-            } catch (o) {
-                o && (a = "PASS");
-            }
-        })();
+        try {
+            throw 1;
+        } catch (o) {
+            o && (a = "PASS");
+        }
         console.log(a);
     }
     expect_stdout: "PASS"
@@ -1305,9 +1734,7 @@ issue_2630_1: {
     }
     expect: {
         var c = 0;
-        (function() {
-            while (void (c = 1 + ++c));
-        })(),
+        while (void (c = 1 + ++c));
         console.log(c);
     }
     expect_stdout: "2"
@@ -1338,9 +1765,8 @@ issue_2630_2: {
     }
     expect: {
         var c = 0;
-        !function() {
-            while (void (c = 1 + (c += 1)));
-        }(), console.log(c);
+        while (void (c = 1 + (c += 1)));
+        console.log(c);
     }
     expect_stdout: "2"
 }
@@ -1384,7 +1810,7 @@ issue_2630_3: {
 issue_2630_4: {
     options = {
         collapse_vars: true,
-        inline: true,
+        inline: 3,
         reduce_vars: true,
         side_effects: true,
         unused: true,
@@ -1412,6 +1838,35 @@ issue_2630_4: {
 }
 
 issue_2630_5: {
+    options = {
+        collapse_vars: true,
+        inline: true,
+        passes: 2,
+        reduce_vars: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        var x = 3, a = 1, b = 2;
+        (function() {
+            (function f1() {
+                while (--x >= 0 && f2());
+            }());
+            function f2() {
+                a++ + (b += a);
+            }
+        })();
+        console.log(a);
+    }
+    expect: {
+        var x = 3, a = 1, b = 2;
+        while (--x >= 0 && void (b += ++a));
+        console.log(a);
+    }
+    expect_stdout: "2"
+}
+
+issue_2630_6: {
     options = {
         assignments: true,
         collapse_vars: true,
@@ -4981,10 +5436,9 @@ issue_3833_2: {
         f();
     }
     expect: {
-        (function(a) {
-            while (a);
-            console.log("PASS");
-        })();
+        var a = void 0;
+        while (a);
+        console.log("PASS");
     }
     expect_stdout: "PASS"
 }
@@ -5605,17 +6059,15 @@ issue_4261: {
         try {
             throw 42;
         } catch (e) {
-            (function() {
-                function g() {
-                    // `ReferenceError: e is not defined` on Node.js v0.10
-                    while (void e.p);
-                }
-                while (console.log(g()));
-            })();
+            function g() {
+                // `ReferenceError: e is not defined` on Node.js v4-
+                while (void e.p);
+            }
+            while (console.log(g()));
         }
     }
     expect_stdout: "undefined"
-    node_version: "<0.10 || >=0.12"
+    node_version: ">=6"
 }
 
 issue_4265: {
@@ -5975,9 +6427,8 @@ issue_4659_3: {
             function f() {
                 return a++;
             }
-            (function() {
-                while (!console);
-            })(f && a++);
+            f && a++;
+            while (!console);
             (function() {
                 var a = console && a;
             })();
@@ -7122,9 +7573,7 @@ issue_5237: {
     }
     expect: {
         function f() {
-            (function() {
-                while (console.log(0/0));
-            })();
+            while (console.log(0/0));
             var NaN = console && console.log(NaN);
             return;
         }
index 12643c9..cb806a0 100644 (file)
@@ -356,6 +356,7 @@ mangle_catch_redef_3: {
         try {
             throw 0;
         } catch (o) {
+            // prints "FAIL" if inlined on Node.js v4-
             (function() {
                 function f() {
                     o = "FAIL";
@@ -366,7 +367,8 @@ mangle_catch_redef_3: {
         console.log(o);
     }
     expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
-    expect_stdout: true
+    expect_stdout: "PASS"
+    node_version: ">=6"
 }
 
 mangle_catch_redef_3_toplevel: {
@@ -379,6 +381,7 @@ mangle_catch_redef_3_toplevel: {
         try {
             throw 0;
         } catch (o) {
+            // prints "FAIL" if inlined on Node.js v4-
             (function() {
                 function f() {
                     o = "FAIL";
@@ -389,7 +392,8 @@ mangle_catch_redef_3_toplevel: {
         console.log(o);
     }
     expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
-    expect_stdout: true
+    expect_stdout: "PASS"
+    node_version: ">=6"
 }
 
 mangle_catch_redef_3_ie8: {
@@ -402,6 +406,7 @@ mangle_catch_redef_3_ie8: {
         try {
             throw 0;
         } catch (o) {
+            // prints "FAIL" if inlined on Node.js v4-
             (function() {
                 function f() {
                     o = "FAIL";
@@ -412,7 +417,8 @@ mangle_catch_redef_3_ie8: {
         console.log(o);
     }
     expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
-    expect_stdout: true
+    expect_stdout: "PASS"
+    node_version: ">=6"
 }
 
 mangle_catch_redef_3_ie8_toplevel: {
@@ -425,6 +431,7 @@ mangle_catch_redef_3_ie8_toplevel: {
         try {
             throw 0;
         } catch (o) {
+            // prints "FAIL" if inlined on Node.js v4-
             (function() {
                 function f() {
                     o = "FAIL";
@@ -435,5 +442,6 @@ mangle_catch_redef_3_ie8_toplevel: {
         console.log(o);
     }
     expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
-    expect_stdout: true
+    expect_stdout: "PASS"
+    node_version: ">=6"
 }
index 93b42d7..53744a3 100644 (file)
@@ -1,6 +1,4 @@
 dont_mangle_arguments: {
-    mangle = {
-    };
     options = {
         booleans: true,
         comparisons: true,
@@ -21,12 +19,13 @@ dont_mangle_arguments: {
         side_effects: true,
         unused: true,
     }
+    mangle = {}
     input: {
         (function(){
             var arguments = arguments, not_arguments = 9;
             console.log(not_arguments, arguments);
-        })(5,6,7);
+        })(5, 6, 7);
     }
-    expect_exact: "(function(){var arguments=arguments,o=9;console.log(o,arguments)})(5,6,7);"
+    expect_exact: "(function(){var arguments,o=9;console.log(o,arguments)})(5,6,7);"
     expect_stdout: true
 }
index 3d6ec41..243517f 100644 (file)
@@ -570,6 +570,50 @@ inlined_assignments: {
     expect_stdout: "PASS"
 }
 
+inilne_for: {
+    options = {
+        inline: true,
+        join_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = function() {
+            for (; console.log("PASS"););
+        };
+        a();
+    }
+    expect: {
+        for (; console.log("PASS"););
+    }
+    expect_stdout: "PASS"
+}
+
+inilne_var: {
+    options = {
+        inline: true,
+        join_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        A = "PASS";
+        var a = function() {
+            var b = A;
+            for (b in console.log(b));
+        };
+        a();
+    }
+    expect: {
+        A = "PASS";
+        var b = A;
+        for (b in console.log(b));
+    }
+    expect_stdout: "PASS"
+}
+
 typescript_enum: {
     rename = true
     options = {
index ea42ce2..7c78ad2 100644 (file)
@@ -1236,7 +1236,8 @@ issues_3267_1: {
     }
     expect: {
         !function() {
-            if (Object())
+            var i = Object();
+            if (i)
                 return console.log("PASS");
             throw "FAIL";
         }();
index 3f4f75d..5ecef2e 100644 (file)
@@ -1054,12 +1054,10 @@ issue_4202: {
         "use strict";
         {
             let o = {};
-            (function() {
-                function f() {
-                    o.p = 42;
-                }
-                f(f);
-            })();
+            function f() {
+                o.p = 42;
+            }
+            f(f);
             console.log(o.p++);
         }
     }
index 6ff4c3c..be9c752 100644 (file)
@@ -262,6 +262,30 @@ de_morgan_2e: {
     node_version: ">=14"
 }
 
+inline_binary_nullish: {
+    options = {
+        inline: true,
+    }
+    input: {
+        (function() {
+            while (console.log("foo"));
+        })() ?? (function() {
+            while (console.log("bar"));
+        })();
+    }
+    expect: {
+        if (void 0 === function() {
+            while (console.log("foo"));
+        }())
+            while (console.log("bar"));
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+    node_version: ">=14"
+}
+
 issue_4679: {
     options = {
         comparisons: true,
index 0ac1592..e38d5f2 100644 (file)
@@ -1363,9 +1363,8 @@ issue_3490_1: {
     }
     expect: {
         var b = 42, c = "FAIL";
-        if (function() {
-            var a;
-        }(), c = "PASS", b) while ("" == typeof d);
+        var a;
+        if (c = "PASS", b) while ("" == typeof d);
         console.log(c, b);
     }
     expect_stdout: "PASS 42"
index 0e232ee..4ae2454 100644 (file)
@@ -41,10 +41,8 @@ reduce_vars: {
     }
     expect: {
         var A = 1;
-        (function() {
-            console.log(-3);
-            console.log(A - 5);
-        })();
+        console.log(-3);
+        console.log(A - 5);
         (function f1() {
             var a = 2;
             console.log(a - 5);
@@ -6690,7 +6688,8 @@ issues_3267_1: {
     }
     expect: {
         !function(x) {
-            if (Object())
+            var i = Object();
+            if (i)
                 return console.log("PASS");
             throw "FAIL";
         }();
index 556e2d7..efd8150 100644 (file)
@@ -1160,10 +1160,8 @@ issue_3490_1: {
     }
     expect: {
         var b = 42, c = "FAIL";
-        if (function() {
-            var a;
-            a && a.p;
-        }(), c = "PASS", b) while ("" == typeof d);
+        var a;
+        if (a && a.p, c = "PASS", b) while ("" == typeof d);
         console.log(c, b);
     }
     expect_stdout: "PASS 42"
@@ -1193,9 +1191,8 @@ issue_3490_2: {
     }
     expect: {
         var b = 42, c = "FAIL";
-        for (function() {
-            var a;
-        }(), c = "PASS", b; "" == typeof d;);
+        var a;
+        for (c = "PASS", b; "" == typeof d;);
         console.log(c, b);
     }
     expect_stdout: "PASS 42"
index dc77f2e..13b12af 100644 (file)
@@ -197,10 +197,8 @@ do_inline_3: {
         })();
     }
     expect: {
-        (function() {
-            var [] = [ ..."" ];
-            while (console.log("PASS"));
-        })();
+        var [] = [ ..."" ];
+        while (console.log("PASS"));
     }
     expect_stdout: "PASS"
     node_version: ">=6"
index 9fb10a1..66fe3a4 100644 (file)
@@ -2,6 +2,7 @@ var assert = require("assert");
 var exec = require("child_process").exec;
 var fs = require("fs");
 var run_code = require("../sandbox").run_code;
+var semver = require("semver");
 var to_ascii = require("../node").to_ascii;
 
 function read(path) {
@@ -12,10 +13,13 @@ describe("bin/uglifyjs", function() {
     var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
     it("Should produce a functional build when using --self", function(done) {
         this.timeout(30000);
-        var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
-        exec(command, {
-            maxBuffer: 1048576
-        }, function(err, stdout) {
+        var command = [
+            uglifyjscmd,
+            "--self",
+            semver.satisfies(process.version, "<=0.12") ? "-mc hoist_funs" : "-mc",
+            "--wrap WrappedUglifyJS",
+        ].join(" ");
+        exec(command, { maxBuffer: 1048576 }, function(err, stdout) {
             if (err) throw err;
             eval(stdout);
             assert.strictEqual(typeof WrappedUglifyJS, "object");
index e99ce61..79b0f29 100644 (file)
@@ -1,14 +1,26 @@
 var assert = require("assert");
 var exec = require("child_process").exec;
+var semver = require("semver");
 var UglifyJS = require("../..");
 
 describe("spidermonkey export/import sanity test", function() {
     it("Should produce a functional build when using --self with spidermonkey", function(done) {
         this.timeout(120000);
         var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
+        var options = semver.satisfies(process.version, "<=0.12") ? "-mc hoist_funs" : "-mc";
         var command = [
-            uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey",
-            uglifyjs + " -p spidermonkey -cm",
+            [
+                uglifyjs,
+                "--self",
+                options,
+                "--wrap SpiderUglify",
+                "-o spidermonkey",
+            ].join(" "),
+            [
+                uglifyjs,
+                "-p spidermonkey",
+                options,
+            ].join(" "),
         ].join(" | ");
         exec(command, { maxBuffer: 1048576 }, function(err, stdout) {
             if (err) throw err;