diff --git a/lib/compress.js b/lib/compress.js index 850866ca..5a8f9ad5 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1397,9 +1397,7 @@ Compressor.prototype.compress = function(node) { operator: "+", expression: make_ref(exp, fixed) }), - right: make_node(AST_Number, node, { - value: 1 - }) + right: make_node(AST_Number, node, { value: 1 }), }); }; d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : []; @@ -1410,7 +1408,7 @@ Compressor.prototype.compress = function(node) { exp.fixed = function() { return make_node(AST_UnaryPrefix, node, { operator: "+", - expression: make_ref(exp, fixed) + expression: make_ref(exp, fixed), }); }; exp.fixed.assigns = fixed && fixed.assigns; @@ -1501,11 +1499,11 @@ Compressor.prototype.compress = function(node) { var fixed = this.definition().fixed; if (fixed) { if (this.fixed) fixed = this.fixed; - return fixed instanceof AST_Node ? fixed : fixed(); + return (fixed instanceof AST_Node ? fixed : fixed()).tail_node(); } fixed = fixed === 0 && this.fixed; if (!fixed) return fixed; - var value = fixed instanceof AST_Node ? fixed : fixed(); + var value = (fixed instanceof AST_Node ? fixed : fixed()).tail_node(); return value.is_constant() && value; }); @@ -1699,7 +1697,7 @@ Compressor.prototype.compress = function(node) { function merge_sequence(array, node) { if (node instanceof AST_Sequence) { - array.push.apply(array, node.expressions); + [].push.apply(array, node.expressions); } else { array.push(node); } @@ -1813,6 +1811,31 @@ Compressor.prototype.compress = function(node) { return true; } + // Certain combination of unused name + side effect leads to invalid AST: + // https://github.com/mishoo/UglifyJS/issues/44 + // https://github.com/mishoo/UglifyJS/issues/1838 + // https://github.com/mishoo/UglifyJS/issues/3371 + // We fix it at this stage by moving the `var` outside the `for`. + function patch_for_init(node, in_list) { + var block; + if (node.init instanceof AST_BlockStatement) { + block = node.init; + node.init = block.body.pop(); + block.body.push(node); + } + if (node.init instanceof AST_Defun) { + if (!block) block = make_node(AST_BlockStatement, node, { body: [ node ] }); + block.body.splice(-1, 0, node.init); + node.init = null; + } else if (node.init instanceof AST_SimpleStatement) { + node.init = node.init.body; + } else if (is_empty(node.init)) { + node.init = null; + } + if (!block) return; + return in_list ? List.splice(block.body) : block; + } + function tighten_body(statements, compressor) { var in_lambda = last_of(compressor, function(node) { return node instanceof AST_Lambda; @@ -1972,12 +1995,12 @@ Compressor.prototype.compress = function(node) { var def = candidate.name.definition(); if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { def.replaced++; - return maintain_this_binding(compressor, parent, node, candidate.value); + return maintain_this_binding(compressor, parent, node, rvalue); } return make_node(AST_Assign, candidate, { operator: "=", left: make_node(AST_SymbolRef, candidate.name, candidate.name), - right: candidate.value, + right: rvalue, }); } var assign = candidate; @@ -1986,7 +2009,9 @@ Compressor.prototype.compress = function(node) { if (!(assign instanceof AST_Assign)) break; assign = assign.right; } - return candidate; + assign = candidate.clone(); + assign.right = rvalue; + return assign; } // These node types have child nodes that execute sequentially, // but are otherwise not safe to scan into or beyond them. @@ -2083,7 +2108,9 @@ Compressor.prototype.compress = function(node) { } // Skip (non-executed) functions and (leading) default case in switch statements if (node instanceof AST_Default || node instanceof AST_Scope) return node; - }, patch_sequence); + }, function(node) { + return patch_sequence(node, multi_replacer); + }); while (--stat_index >= 0) { // Treat parameters as collapsible in IIFE, i.e. // function(a, b){ ... }(x()); @@ -2122,7 +2149,14 @@ Compressor.prototype.compress = function(node) { var well_defined = true; var lvalues = get_lvalues(candidate); var lhs_local = is_lhs_local(lhs); - var rvalue = get_rvalue(candidate); + var rhs_value = get_rvalue(candidate); + var rvalue; + if (rhs_value instanceof AST_Sequence + && !(candidate instanceof AST_Assign && candidate.operator != "=")) { + rvalue = rhs_value.tail_node(); + } else { + rvalue = rhs_value; + } if (!side_effects) side_effects = value_has_side_effects(); var check_destructured = in_try || !lhs_local ? function(node) { return node instanceof AST_Destructured; @@ -2163,7 +2197,7 @@ Compressor.prototype.compress = function(node) { value_def.single_use = false; changed = true; } - if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1); + if (replaced) remove_candidate(candidate); } } return changed; @@ -2768,6 +2802,7 @@ Compressor.prototype.compress = function(node) { return; } if (remaining < 1) return; + rhs = rhs.tail_node(); var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs; if (!(value instanceof AST_SymbolRef)) return; var def = value.definition(); @@ -3002,24 +3037,21 @@ Compressor.prototype.compress = function(node) { } function remove_candidate(expr) { + var value = rvalue === rhs_value ? null : make_sequence(rhs_value, rhs_value.expressions.slice(0, -1)); var index = expr.name_index; if (index >= 0) { var argname = scope.argnames[index]; if (argname instanceof AST_DefaultValue) { - argname.value = make_node(AST_Number, argname, { - value: 0 - }); + argname.value = value || make_node(AST_Number, argname, { value: 0 }); argname.name.definition().fixed = false; } else { var args = compressor.parent().args; if (args[index]) { - args[index] = make_node(AST_Number, args[index], { - value: 0 - }); + args[index] = value || make_node(AST_Number, args[index], { value: 0 }); argname.definition().fixed = false; } } - return true; + return; } var end = hit_stack.length - 1; if (hit_stack[end - 1].body === hit_stack[end]) end--; @@ -3034,20 +3066,46 @@ Compressor.prototype.compress = function(node) { if (value_def) value_def.replaced++; node = node.clone(); node.value = null; - return node; + return value ? List.splice([ value, node ]) : node; } - return in_list ? List.skip : null; - }, patch_sequence); + if (!value) return in_list ? List.skip : null; + return is_statement(node) ? make_node(AST_SimpleStatement, value, { body: value }) : value; + }, function(node, in_list) { + if (node instanceof AST_Definitions) { + var body = [], defns = node.definitions; + for (var index = 0, pos = 0; index < defns.length; index++) { + var defn = defns[index]; + if (defn instanceof AST_VarDef) continue; + flush(); + pos = index + 1; + body.push(make_node(AST_SimpleStatement, defn, { body: defn })); + } + if (pos == 0) return; + flush(); + if (body.length == 1) return body[0]; + return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, { body: body }); + } + if (node instanceof AST_For) return patch_for_init(node, in_list); + return patch_sequence(node, this); + + function flush() { + if (pos < index) { + var cropped = node.clone(); + cropped.definitions = defns.slice(pos, index); + body.push(cropped); + } + } + }); abort = false; hit = false; hit_index = 0; - return statements[stat_index].transform(tt); + if (!(statements[stat_index] = statements[stat_index].transform(tt))) statements.splice(stat_index, 1); } - function patch_sequence(node) { + function patch_sequence(node, tt) { if (node instanceof AST_Sequence) switch (node.expressions.length) { case 0: return null; - case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]); + case 1: return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]); } } @@ -3779,11 +3837,18 @@ Compressor.prototype.compress = function(node) { } else if (stat instanceof AST_If) { stat.condition = join_assigns_expr(stat.condition); } else if (stat instanceof AST_SimpleStatement) { - var exprs = join_assigns(prev, stat.body); + var exprs = join_assigns(prev, stat.body), next; if (exprs) { changed = true; if (!exprs.length) continue; stat.body = make_sequence(stat.body, exprs); + } else if (prev instanceof AST_Definitions + && (next = statements[i + 1]) + && prev.TYPE == next.TYPE + && (next = next.definitions[0]).value) { + changed = true; + next.value = make_sequence(stat, [ stat.body, next.value ]); + continue; } } else if (stat instanceof AST_Switch) { stat.expression = join_assigns_expr(stat.expression); @@ -6690,7 +6755,7 @@ Compressor.prototype.compress = function(node) { value = null; trim_defns.push(def); } - var old_def; + var old_def, fn; if (!value && !(node instanceof AST_Let)) { if (parent instanceof AST_ExportDeclaration) { flush(); @@ -6704,17 +6769,18 @@ Compressor.prototype.compress = function(node) { } else if (compressor.option("functions") && !compressor.option("ie") && drop_sym + && value && var_defs[sym.id] == 1 && sym.assignments == 0 - && value instanceof AST_LambdaExpression + && (fn = value.tail_node()) instanceof AST_LambdaExpression && !is_arguments(sym) - && !is_arrow(value) - && assigned_once(value, sym.references) - && can_declare_defun(value) - && (old_def = rename_def(value, def.name.name)) !== false) { + && !is_arrow(fn) + && assigned_once(fn, sym.references) + && can_declare_defun(fn) + && (old_def = rename_def(fn, def.name.name)) !== false) { AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name)); var ctor; - switch (value.CTOR) { + switch (fn.CTOR) { case AST_AsyncFunction: ctor = AST_AsyncDefun; break; @@ -6728,7 +6794,7 @@ Compressor.prototype.compress = function(node) { ctor = AST_GeneratorDefun; break; } - var defun = make_node(ctor, def, value); + var defun = make_node(ctor, def, fn); defun.name = make_node(AST_SymbolDefun, def.name, def.name); var name_def = def.name.scope.resolve().def_function(defun.name); if (old_def) old_def.forEach(function(node) { @@ -6737,6 +6803,7 @@ Compressor.prototype.compress = function(node) { node.reference(); }); body.push(defun); + if (value !== fn) [].push.apply(side_effects, value.expressions.slice(0, -1)); } else { if (drop_sym && var_defs[sym.id] > 1 @@ -6759,7 +6826,7 @@ Compressor.prototype.compress = function(node) { head.push(def); } } else { - value = value && !value.single_use && value.drop_side_effect_free(compressor); + value = value && value.drop_side_effect_free(compressor); if (value) { AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name)); side_effects.push(value); @@ -6867,13 +6934,18 @@ Compressor.prototype.compress = function(node) { } } default: + var seq; + if (tail.length > 0 && (seq = tail[0].value) instanceof AST_Sequence) { + tail[0].value = seq.tail_node(); + body.push(make_node(AST_SimpleStatement, node, { + body: make_sequence(seq, seq.expressions.slice(0, -1)), + })); + } node.definitions = head.concat(tail); body.push(node); } if (side_effects.length > 0) { - body.push(make_node(AST_SimpleStatement, node, { - body: make_sequence(node, side_effects) - })); + body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) })); } return insert_statements(body, node, in_list); } @@ -6922,33 +6994,7 @@ Compressor.prototype.compress = function(node) { } }, function(node, in_list) { if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list); - // Certain combination of unused name + side effect leads to invalid AST: - // https://github.com/mishoo/UglifyJS/issues/44 - // https://github.com/mishoo/UglifyJS/issues/1838 - // https://github.com/mishoo/UglifyJS/issues/3371 - // We fix it at this stage by moving the `var` outside the `for`. - if (node instanceof AST_For) { - var block; - if (node.init instanceof AST_BlockStatement) { - block = node.init; - node.init = block.body.pop(); - block.body.push(node); - } - if (node.init instanceof AST_Defun) { - if (!block) { - block = make_node(AST_BlockStatement, node, { - body: [ node ] - }); - } - block.body.splice(-1, 0, node.init); - node.init = null; - } else if (node.init instanceof AST_SimpleStatement) { - node.init = node.init.body; - } else if (is_empty(node.init)) { - node.init = null; - } - return !block ? node : in_list ? List.splice(block.body) : block; - } + if (node instanceof AST_For) return patch_for_init(node, in_list); if (node instanceof AST_ForIn) { if (!drop_vars || !compressor.option("loops")) return; if (!is_empty(node.body)) return; @@ -8749,15 +8795,21 @@ Compressor.prototype.compress = function(node) { var body = [], var_defs = [], refs = []; var body_exprs = sequencesize(self.body, body, var_defs, refs); var alt_exprs = sequencesize(self.alternative, body, var_defs, refs); - if (body_exprs && alt_exprs) { + if (body_exprs instanceof AST_BlockStatement || alt_exprs instanceof AST_BlockStatement) { + if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs })); + body.push(self); + if (body_exprs instanceof AST_BlockStatement) self.body = body_exprs; + if (alt_exprs instanceof AST_BlockStatement) self.alternative = alt_exprs; + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + } else if (body_exprs && alt_exprs) { if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs })); if (body_exprs.length == 0) { body.push(make_node(AST_SimpleStatement, self.condition, { body: alt_exprs.length > 0 ? make_node(AST_Binary, self, { - operator : "||", - left : self.condition, - right : make_sequence(self.alternative, alt_exprs) - }).transform(compressor) : self.condition.clone() + operator: "||", + left: self.condition, + right: make_sequence(self.alternative, alt_exprs), + }).transform(compressor) : self.condition.clone(), }).optimize(compressor)); } else if (alt_exprs.length == 0) { if (self_condition_length === negated_length && !negated_is_best @@ -8769,79 +8821,64 @@ Compressor.prototype.compress = function(node) { } body.push(make_node(AST_SimpleStatement, self, { body: make_node(AST_Binary, self, { - operator : negated_is_best ? "||" : "&&", - left : negated_is_best ? negated : self.condition, - right : make_sequence(self.body, body_exprs) - }).transform(compressor) + operator: negated_is_best ? "||" : "&&", + left: negated_is_best ? negated : self.condition, + right: make_sequence(self.body, body_exprs), + }).transform(compressor), }).optimize(compressor)); } else { body.push(make_node(AST_SimpleStatement, self, { body: make_node(AST_Conditional, self, { - condition : self.condition, - consequent : make_sequence(self.body, body_exprs), - alternative : make_sequence(self.alternative, alt_exprs) - }) + condition: self.condition, + consequent: make_sequence(self.body, body_exprs), + alternative: make_sequence(self.alternative, alt_exprs), + }), }).optimize(compressor)); } refs.forEach(function(ref) { ref.definition().references.push(ref); }); - return make_node(AST_BlockStatement, self, { - body: body - }).optimize(compressor); + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); } - if (is_empty(self.body)) { - self = make_node(AST_If, self, { - condition: negated, - body: self.alternative, - alternative: null - }); - } - if (self.body instanceof AST_Exit - && self.alternative instanceof AST_Exit - && self.body.TYPE == self.alternative.TYPE) { + if (is_empty(self.body)) self = make_node(AST_If, self, { + condition: negated, + body: self.alternative, + alternative: null, + }); + if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) { var exit = make_node(self.body.CTOR, self, { value: make_node(AST_Conditional, self, { - condition : self.condition, - consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor), - alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor) - }) + condition: self.condition, + consequent: self.body.value || make_node(AST_Undefined, self.body).transform(compressor), + alternative: self.alternative.value + || make_node(AST_Undefined, self.alternative).transform(compressor), + }), }); - if (exit instanceof AST_Return) { - exit.in_bool = self.body.in_bool || self.alternative.in_bool; - } + if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool; return exit; } - if (self.body instanceof AST_If - && !self.body.alternative - && !self.alternative) { + if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) { self = make_node(AST_If, self, { condition: make_node(AST_Binary, self.condition, { operator: "&&", left: self.condition, - right: self.body.condition + right: self.body.condition, }), body: self.body.body, - alternative: null + alternative: null, }); } - if (aborts(self.body)) { - if (self.alternative) { - var alt = self.alternative; - self.alternative = null; - return make_node(AST_BlockStatement, self, { - body: [ self, alt ] - }).optimize(compressor); - } + if (aborts(self.body) && self.alternative) { + var alt = self.alternative; + self.alternative = null; + return make_node(AST_BlockStatement, self, { body: [ self, alt ] }).optimize(compressor); } if (aborts(self.alternative)) { var body = self.body; self.body = self.alternative; self.condition = negated_is_best ? negated : self.condition.negate(compressor); self.alternative = null; - return make_node(AST_BlockStatement, self, { - body: [ self, body ] - }).optimize(compressor); + return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor); } if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative); return self; @@ -8852,10 +8889,19 @@ Compressor.prototype.compress = function(node) { var exprs = []; for (var i = 0; i < stat.body.length; i++) { var line = stat.body[i]; + if (line instanceof AST_EmptyStatement) continue; + if (line instanceof AST_Exit) { + if (exprs.length == 0) return; + line = line.clone(); + exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor)); + line.value = make_sequence(stat, exprs); + var block = stat.clone(); + block.body = block.body.slice(i + 1); + block.body.unshift(line); + return block; + } if (line instanceof AST_LambdaDefinition) { defuns.push(line); - } else if (line instanceof AST_EmptyStatement) { - continue; } else if (line instanceof AST_SimpleStatement) { if (!compressor.option("sequences") && exprs.length > 0) return; exprs.push(line.body); @@ -9320,9 +9366,7 @@ Compressor.prototype.compress = function(node) { args[pos++] = make_sequence(call, side_effects); side_effects = []; } else { - args[pos++] = make_node(AST_Number, args[i], { - value: 0 - }); + args[pos++] = make_node(AST_Number, args[i], { value: 0 }); continue; } } @@ -9924,10 +9968,14 @@ Compressor.prototype.compress = function(node) { for (var i = 0; i < len; i++) { var line = fn.body[i]; if (line instanceof AST_Var) { - var assigned = var_assigned || !declarations_only(line); - if (assigned) { + if (var_assigned) { + if (!stat) continue; + if (!(stat instanceof AST_SimpleStatement)) return false; + if (!declarations_only(line)) stat = null; + } else if (!declarations_only(line)) { + if (stat && !(stat instanceof AST_SimpleStatement)) return false; + stat = null; var_assigned = true; - if (stat) return false; } } else if (line instanceof AST_AsyncDefun || line instanceof AST_Defun @@ -10191,7 +10239,7 @@ Compressor.prototype.compress = function(node) { function flatten_vars(decls, expressions) { var args = [ insert, 0 ]; - var decl_var = [], expr_var = [], expr_loop = []; + 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) { @@ -10209,11 +10257,20 @@ Compressor.prototype.compress = function(node) { } continue; } - if (!(stat instanceof AST_Var)) continue; + if (!(stat instanceof AST_Var)) { + if (stat instanceof AST_SimpleStatement) exprs.push(stat.body); + continue; + } for (var j = 0; j < stat.definitions.length; j++) { var var_def = stat.definitions[j]; var name = flatten_var(var_def.name); - append_var(decl_var, expr_var, name, var_def.value); + var value = var_def.value; + if (value && exprs.length > 0) { + exprs.push(value); + value = make_sequence(var_def, exprs); + exprs = []; + } + append_var(decl_var, expr_var, name, value); if (in_loop && !arg_used.has(name.name)) { var def = fn.variables.get(name.name); var sym = make_node(AST_SymbolRef, name, name); @@ -12211,9 +12268,7 @@ Compressor.prototype.compress = function(node) { } function pop_seq(node) { - if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { - value: 0 - }); + if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { value: 0 }); return make_sequence(node, node.expressions.slice(0, -1)); } }); @@ -12526,35 +12581,25 @@ Compressor.prototype.compress = function(node) { var exp = self.expression.expression; if (is_undeclared_ref(exp)) switch (exp.name) { case "Array": - self.expression = make_node(AST_Array, self.expression, { - elements: [] - }); + self.expression = make_node(AST_Array, self.expression, { elements: [] }); break; case "Function": self.expression = make_node(AST_Function, self.expression, { argnames: [], - body: [] + body: [], }).init_vars(exp.scope); break; case "Number": - self.expression = make_node(AST_Number, self.expression, { - value: 0 - }); + self.expression = make_node(AST_Number, self.expression, { value: 0 }); break; case "Object": - self.expression = make_node(AST_Object, self.expression, { - properties: [] - }); + self.expression = make_node(AST_Object, self.expression, { properties: [] }); break; case "RegExp": - self.expression = make_node(AST_RegExp, self.expression, { - value: /t/ - }); + self.expression = make_node(AST_RegExp, self.expression, { value: /t/ }); break; case "String": - self.expression = make_node(AST_String, self.expression, { - value: "" - }); + self.expression = make_node(AST_String, self.expression, { value: "" }); break; } } diff --git a/test/compress/assignments.js b/test/compress/assignments.js index 37c67411..9e831721 100644 --- a/test/compress/assignments.js +++ b/test/compress/assignments.js @@ -489,7 +489,7 @@ logical_assignments: { node_version: ">=15" } -logical_collapse_vars: { +logical_collapse_vars_1: { options = { collapse_vars: true, } @@ -509,6 +509,27 @@ logical_collapse_vars: { node_version: ">=15" } +logical_collapse_vars_2: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS"; + (function(b) { + b ||= (a = "FAIL", {}); + return b; + })(console).log(a); + } + expect: { + var a = "PASS"; + (function(b) { + return b ||= (a = "FAIL", {}); + })(console).log(a); + } + expect_stdout: "PASS" + node_version: ">=15" +} + logical_reduce_vars: { options = { evaluate: true, diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index c0485590..03a375bd 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -346,9 +346,7 @@ collapse_vars_if: { return "x" != "Bar" + x / 4 ? g9 : g5; } function f3(x) { - if (x) - return 1; - return 2; + return x ? 1 : 2; } } } @@ -1912,6 +1910,42 @@ collapse_vars_regexp: { ] } +collapse_arg_sequence: { + options = { + collapse_vars: true, + unused: true, + } + input: { + (function(a) { + a("foo"); + })((console.log("bar"), console.log)); + } + expect: { + (function(a) { + (0, console.log)("foo"); + })(console.log("bar")); + } + expect_stdout: [ + "bar", + "foo", + ] +} + +collapse_for_init: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + for (var a = (Math, console), b = a.log("PASS"); b;); + } + expect: { + Math; + for (var a, b = console.log("PASS"); b;); + } + expect_stdout: "PASS" +} + issue_1537: { options = { collapse_vars: true, @@ -2667,6 +2701,24 @@ chained_4: { expect_stdout: "foo undefined" } +chained_5: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS"; + var a = (console, console.log(a)); + a && ++a; + } + expect: { + var a = "PASS"; + console; + var a; + (a = console.log(a)) && ++a; + } + expect_stdout: "PASS" +} + boolean_binary_1: { options = { collapse_vars: true, @@ -2906,6 +2958,43 @@ compound_assignment_2: { expect_stdout: "4" } +compound_assignment_3: { + options = { + collapse_vars: true, + } + input: { + var a = 1; + a += (console.log("PASS"), 2); + a.p; + } + expect: { + var a = 1; + (a += (console.log("PASS"), 2)).p; + } + expect_stdout: "PASS" +} + +compound_assignment_4: { + options = { + collapse_vars: true, + evaluate: true, + } + input: { + A = "PASS"; + var a = ""; + a += (a = "FAIL", A); + a.p; + console.log(a); + } + expect: { + A = "PASS"; + var a = ""; + (a += (a = "FAIL", A)).p; + console.log(a); + } + expect_stdout: "PASS" +} + issue_2187_1: { options = { collapse_vars: true, @@ -3030,10 +3119,10 @@ issue_2203_2: { a: "FAIL", b: function() { return function(c) { - return (String, (Object, function() { + return (Object, function() { return this; - }())).a; - }(); + }()).a; + }(String); } }.b()); } @@ -3764,17 +3853,17 @@ issue_2437_1: { console.log(foo()); } expect: { - console.log(function() { - if (xhrDesc) { - var result = !!(req = new XMLHttpRequest()).onreadystatechange; - return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}), - result; - } - var req = new XMLHttpRequest(), detectFunc = function(){}; - return req.onreadystatechange = detectFunc, - result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc, - req.onreadystatechange = null, result; - }()); + var req, detectFunc, result; + console.log(( + xhrDesc ? (result = !!(req = new XMLHttpRequest).onreadystatechange, + Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc||{}) + ) : ( + (req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){}, + result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc, + req.onreadystatechange = null + ), + result + )); } } @@ -3815,15 +3904,15 @@ issue_2437_2: { foo(); } expect: { - !function() { - if (xhrDesc) - return (req = new XMLHttpRequest()).onreadystatechange, - Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}); - var req = new XMLHttpRequest(); - req.onreadystatechange = function(){}, + var req; + xhrDesc ? ( + (req = new XMLHttpRequest).onreadystatechange, + Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}) + ) : ( + (req = new XMLHttpRequest).onreadystatechange = function(){}, req[SYMBOL_FAKE_ONREADYSTATECHANGE_1], - req.onreadystatechange = null; - }(); + req.onreadystatechange = null + ); } } @@ -6445,7 +6534,8 @@ assign_undeclared: { console.log(typeof B); } expect: { - B = new (console.log(42), function() {})(); + console.log(42); + B = new function() {}(); console.log(typeof B); } expect_stdout: [ @@ -9141,9 +9231,7 @@ issue_4908: { console.log(d[1]); } expect: { - var a = 0, b; - console || a++; - var c = a, d = [ (d = a) && d, d += 42 ]; + var a = 0, b, c = (console || a++, a), d = [ (d = a) && d, d += 42 ]; console.log(d[1]); } expect_stdout: "42" diff --git a/test/compress/default-values.js b/test/compress/default-values.js index b888672f..f43374b7 100644 --- a/test/compress/default-values.js +++ b/test/compress/default-values.js @@ -158,6 +158,28 @@ process_boolean_returns: { node_version: ">=6" } +collapse_arg_sequence: { + options = { + collapse_vars: true, + unused: true, + } + input: { + (function(a = (console.log("bar"), console.log)) { + a("foo"); + })(); + } + expect: { + (function(a = console.log("bar")) { + (0, console.log)("foo"); + })(); + } + expect_stdout: [ + "bar", + "foo", + ] + node_version: ">=6" +} + collapse_value_1: { options = { collapse_vars: true, diff --git a/test/compress/destructured.js b/test/compress/destructured.js index a13d920d..f246500a 100644 --- a/test/compress/destructured.js +++ b/test/compress/destructured.js @@ -395,7 +395,8 @@ funarg_unused_6_keep_fargs: { } expect: { (function() { - var {} = (console, 42); + console; + var {} = 42; })(); console.log(typeof a); } diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 2c4b7350..324c8f37 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1728,7 +1728,8 @@ issue_2768: { } expect: { var a = "FAIL"; - var c = (d = a, void (d && (a = "PASS"))); + d = a; + var c = void (d && (a = "PASS")); var d; console.log(a, typeof c); } @@ -2382,7 +2383,8 @@ issue_3664: { } expect: { console.log(function() { - var a, b = (a = (a = [ b && console.log("FAIL") ]).p = 0, 0); + a = (a = [ b && console.log("FAIL") ]).p = 0; + var a, b = 0; return "PASS"; }()); } @@ -2551,10 +2553,9 @@ issue_3899: { console.log(typeof a); } expect: { - function a() { + console.log(typeof function() { return 2; - } - console.log(typeof a); + }); } expect_stdout: "function" } diff --git a/test/compress/exponentiation.js b/test/compress/exponentiation.js index 702add8c..995dc8c6 100644 --- a/test/compress/exponentiation.js +++ b/test/compress/exponentiation.js @@ -99,8 +99,8 @@ issue_4664: { expect: { (function f() { new function(a) { - console.log(typeof f, a, typeof this); - }((A = 0, 2 ** 30)); + console.log(typeof f, 2 ** 30, typeof this); + }(A = 0); })(); } expect_stdout: "function 1073741824 object" diff --git a/test/compress/functions.js b/test/compress/functions.js index dff92d0c..e9c25474 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -3039,18 +3039,17 @@ issue_2437: { console.log(foo()); } expect: { - console.log(function() { - if (xhrDesc) { - var result = !!(req = new XMLHttpRequest()).onreadystatechange; - return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}), - result; - } - function detectFunc() {} - var req = new XMLHttpRequest(); - return req.onreadystatechange = detectFunc, - result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc, - req.onreadystatechange = null, result; - }()); + var req, detectFunc, result; + console.log(( + xhrDesc ? ( + result = !!(req = new XMLHttpRequest).onreadystatechange, + Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}) + ) : ( + (req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){}, + result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,req.onreadystatechange = null + ), + result + )); } } @@ -3826,16 +3825,14 @@ inlined_single_use: { } expect: { console.log(function(f) { - var a = function() { + a = function() { A; - }; - var b = function() { + }, + b = function() { a(B); - }; - (function() { - b; - }); - return; + }, + void 0; + var a, b; }()); } expect_stdout: "undefined" @@ -5290,6 +5287,42 @@ direct_inline_catch_redefined: { expect_stdout: true } +statement_var_inline: { + options = { + inline: true, + join_vars: true, + unused: true, + } + input: { + function f() { + (function() { + var a = {}; + function g() { + a.p; + } + g(console.log("PASS")); + var b = function h(c) { + c && c.q; + }(); + })(); + } + f(); + } + expect: { + function f() { + var c, a = {}; + function g() { + a.p; + } + g(console.log("PASS")); + c && c.q; + return; + } + f(); + } + expect_stdout: "PASS" +} + issue_4171_1: { options = { functions: true, diff --git a/test/compress/hoist_vars.js b/test/compress/hoist_vars.js index 3a2844a8..b9b72c54 100644 --- a/test/compress/hoist_vars.js +++ b/test/compress/hoist_vars.js @@ -282,7 +282,7 @@ issue_4736: { (function() { (function() { 0, - console.log(1073741824); + console.log(1 << 30); })(); })(); } @@ -336,7 +336,7 @@ issue_4859: { } expect: { (function f(a) { - console.log(Infinity); + console.log(2 + 1 / 0); return f; })(); } diff --git a/test/compress/join_vars.js b/test/compress/join_vars.js index e2c54be1..3d6ec410 100644 --- a/test/compress/join_vars.js +++ b/test/compress/join_vars.js @@ -1282,10 +1282,7 @@ assign_sequence_var: { console.log(a, b, c); } expect: { - var a = 0, b = 1; - console.log(a), - a++; - var b = 2, c = 3; + var a = 0, b = 1, c = (console.log(a), a++, b = 2, 3); console.log(a, b, c); } expect_stdout: [ diff --git a/test/compress/keep_fargs.js b/test/compress/keep_fargs.js index 2c7bf274..ea42ce2d 100644 --- a/test/compress/keep_fargs.js +++ b/test/compress/keep_fargs.js @@ -181,10 +181,10 @@ issue_2203_2: { a: "FAIL", b: function() { return function() { - return (String, (Object, function() { + return (Object, function() { return this; - }())).a; - }(); + }()).a; + }(String); } }.b()); } diff --git a/test/compress/sequences.js b/test/compress/sequences.js index cc05fd7b..556e2d70 100644 --- a/test/compress/sequences.js +++ b/test/compress/sequences.js @@ -910,9 +910,7 @@ hoist_decl: { var d; } expect: { - var a; - w(); - var b = x(), c, d; + var a, b = (w(), x()), c, d; for (y(); 0;) z(); } } diff --git a/test/mocha/sourcemaps.js b/test/mocha/sourcemaps.js index 24a60f23..dbe542eb 100644 --- a/test/mocha/sourcemaps.js +++ b/test/mocha/sourcemaps.js @@ -205,8 +205,8 @@ describe("sourcemaps", function() { }); if (result.error) throw result.error; assert.strictEqual(result.code, [ - "var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};", - "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=", + "var Foo=function(){console.log(3)},bar=(new Foo,function(o){return o});", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElDQWxDQyxLREEyQyxJQUFJSCxJQ0MvQyxTQUFjRyxHQUNWLE9BQU9BIn0=", ].join("\n")); assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]); });