diff --git a/README.md b/README.md index 8fbe07c4..048de63b 100644 --- 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 diff --git a/lib/ast.js b/lib/ast.js index 12138520..76323482 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -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 ]----- */ diff --git a/lib/compress.js b/lib/compress.js index 949abac5..e4748a30 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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 (!changed && last_changed == 5) break; } - if (compressor.option("join_vars")) { - if (join_consecutive_vars(statements)) changed = 6; + 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 = 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; } diff --git a/test/compress/awaits.js b/test/compress/awaits.js index 34479d48..577474b6 100644 --- a/test/compress/awaits.js +++ b/test/compress/awaits.js @@ -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, diff --git a/test/compress/classes.js b/test/compress/classes.js index 1b1aea16..0b4f622e 100644 --- a/test/compress/classes.js +++ b/test/compress/classes.js @@ -1310,6 +1310,7 @@ issue_4725_1: { issue_4725_2: { options = { + if_return: true, inline: true, } input: { diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index a5b51dc7..c0485590 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -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); diff --git a/test/compress/const.js b/test/compress/const.js index 521a14d8..83da5a4d 100644 --- a/test/compress/const.js +++ b/test/compress/const.js @@ -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, diff --git a/test/compress/default-values.js b/test/compress/default-values.js index 5fc69817..b888672f 100644 --- a/test/compress/default-values.js +++ b/test/compress/default-values.js @@ -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", diff --git a/test/compress/destructured.js b/test/compress/destructured.js index 5acc7f69..a13d920d 100644 --- a/test/compress/destructured.js +++ b/test/compress/destructured.js @@ -2069,7 +2069,7 @@ issue_4319: { issue_4321: { options = { - inline: true, + inline: 3, keep_fargs: false, } input: { diff --git a/test/compress/functions.js b/test/compress/functions.js index 2841f9e6..dff92d0c 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -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: { diff --git a/test/compress/if_return.js b/test/compress/if_return.js index 28e6a57e..e19ca6fa 100644 --- a/test/compress/if_return.js +++ b/test/compress/if_return.js @@ -545,7 +545,36 @@ if_body_return_3: { ] } -issue_3600: { +issue_3600_1: { + options = { + if_return: true, + inline: 3, + 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++]) b = --b, c = 42; + var b; + })(); + console.log(c); + } + expect_stdout: "1" +} + +issue_3600_2: { options = { if_return: true, inline: true, @@ -566,8 +595,10 @@ issue_3600: { expect: { var c = 0; (function() { - if ([][c++]) b = --b, c = 42; - var b; + if ([][c++]) { + var b = --b; + c = 42; + } })(); console.log(c); } diff --git a/test/compress/keep_fargs.js b/test/compress/keep_fargs.js index b69a088b..2c7bf274 100644 --- a/test/compress/keep_fargs.js +++ b/test/compress/keep_fargs.js @@ -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, diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index f98f88e6..7a150afa 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -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, diff --git a/test/compress/spreads.js b/test/compress/spreads.js index 8c34a0da..dc77f2e6 100644 --- a/test/compress/spreads.js +++ b/test/compress/spreads.js @@ -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, diff --git a/test/compress/yields.js b/test/compress/yields.js index f0ffded3..306ec6e4 100644 --- a/test/compress/yields.js +++ b/test/compress/yields.js @@ -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,