diff --git a/bin/uglifyjs b/bin/uglifyjs index cecc98be..311e7138 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -11,7 +11,7 @@ var path = require("path"); var program = require("commander"); var UglifyJS = require("../tools/node"); -var skip_keys = [ "cname", "enclosed", "inlined", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ]; +var skip_keys = [ "cname", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ]; var files = {}; var options = { compress: false, @@ -234,7 +234,33 @@ function run() { } fatal(ex); } else if (program.output == "ast") { + if (!options.compress && !options.mangle) { + result.ast.figure_out_scope({}); + } print(JSON.stringify(result.ast, function(key, value) { + switch (key) { + case "thedef": + if (typeof value == "object" && typeof value.id == "number") { + return value.id; + } + return; + case "enclosed": + return value.map(function(sym){ + return sym.id; + }); + case "variables": + case "functions": + case "globals": + if (value && value.size()) { + var ret = {}; + value.each(function(val, key) { + // key/val inverted for readability. + ret[val.id] = key; + }); + return ret; + } + return; + } if (skip_key(key)) return; if (value instanceof UglifyJS.AST_Token) return; if (value instanceof UglifyJS.Dictionary) return; diff --git a/lib/compress.js b/lib/compress.js index e80d7a99..773c0d60 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -489,8 +489,9 @@ merge(Compressor.prototype, { if (node instanceof AST_SymbolRef) d.references.push(node); d.fixed = false; }); - def(AST_Accessor, function(tw, descend) { + def(AST_Accessor, function(tw, descend, compressor) { push(tw); + reset_variables(tw, compressor, this); descend(); pop(tw); return true; @@ -947,6 +948,7 @@ merge(Compressor.prototype, { } function tighten_body(statements, compressor) { + var scope = compressor.find_parent(AST_Scope).get_defun_scope(); var CHANGED, max_iter = 10; do { CHANGED = false; @@ -978,7 +980,6 @@ merge(Compressor.prototype, { // Will not attempt to collapse assignments into or past code blocks // which are not sequentially executed, e.g. loops and conditionals. function collapse(statements, compressor) { - var scope = compressor.find_parent(AST_Scope).get_defun_scope(); if (scope.uses_eval || scope.uses_with) return statements; var args; var candidates = []; @@ -1640,7 +1641,7 @@ merge(Compressor.prototype, { function next_index(i) { for (var j = i + 1, len = statements.length; j < len; j++) { var stat = statements[j]; - if (!(stat instanceof AST_Definitions && declarations_only(stat))) { + if (!(stat instanceof AST_Var && declarations_only(stat))) { break; } } @@ -1650,7 +1651,7 @@ merge(Compressor.prototype, { function prev_index(i) { for (var j = i; --j >= 0;) { var stat = statements[j]; - if (!(stat instanceof AST_Definitions && declarations_only(stat))) { + if (!(stat instanceof AST_Var && declarations_only(stat))) { break; } } @@ -1813,6 +1814,41 @@ merge(Compressor.prototype, { statements.length = n; } + function join_object_assignments(defn, body) { + if (!(defn instanceof AST_Definitions)) return; + var def = defn.definitions[defn.definitions.length - 1]; + if (!(def.value instanceof AST_Object)) return; + var exprs; + if (body instanceof AST_Assign) { + exprs = [ body ]; + } else if (body instanceof AST_Sequence) { + exprs = body.expressions.slice(); + } + if (!exprs) return; + var trimmed = false; + do { + var node = exprs[0]; + if (!(node instanceof AST_Assign)) break; + if (!(node.left instanceof AST_PropAccess)) break; + var sym = node.left.expression; + if (!(sym instanceof AST_SymbolRef)) break; + if (def.name.name != sym.name) break; + if (!node.right.is_constant_expression(scope)) break; + var prop = node.left.property; + if (prop instanceof AST_Node) { + prop = prop.evaluate(compressor); + } + if (prop instanceof AST_Node) break; + def.value.properties.push(make_node(AST_ObjectKeyVal, node, { + key: prop, + value: node.right + })); + exprs.shift(); + trimmed = true; + } while (exprs.length); + return trimmed && exprs; + } + function join_consecutive_vars(statements, compressor) { var defs; for (var i = 0, j = -1, len = statements.length; i < len; i++) { @@ -1845,6 +1881,14 @@ merge(Compressor.prototype, { } else { statements[++j] = stat; } + } else if (stat instanceof AST_SimpleStatement) { + var exprs = join_object_assignments(prev, stat.body); + if (exprs) { + CHANGED = true; + if (!exprs.length) continue; + stat.body = make_sequence(stat.body, exprs); + } + statements[++j] = stat; } else { statements[++j] = stat; } @@ -2952,7 +2996,7 @@ merge(Compressor.prototype, { var node_def = def.name.definition();; initializations.add(node_def.id, def.value); if (def.name.fixed_value() === def.value) { - fixed_ids[node_def.id] = true; + fixed_ids[node_def.id] = def; } } if (def.value.has_side_effects(compressor)) { @@ -2988,9 +3032,7 @@ merge(Compressor.prototype, { var def = sym.definition(); var in_use = def.id in in_use_ids; if (node instanceof AST_Assign) { - if (!in_use - || def.id in fixed_ids - && node.left.fixed_value() !== node.right) { + if (!in_use || def.id in fixed_ids && fixed_ids[def.id] !== node) { return maintain_this_binding(parent, node, node.right.transform(tt)); } } else if (!in_use) return make_node(AST_Number, node, { @@ -3059,25 +3101,29 @@ merge(Compressor.prototype, { var sym = def.name.definition(); if (drop_block && sym.global) return tail.push(def); if (!(drop_vars || drop_block) || sym.id in in_use_ids) { + if (def.value && sym.id in fixed_ids && fixed_ids[sym.id] !== def) { + def.value = def.value.drop_side_effect_free(compressor); + } if (def.name instanceof AST_SymbolVar) { var var_defs = var_defs_by_id.get(sym.id); if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) { compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name)); if (def.value) { - side_effects.push(make_node(AST_Assign, def, { + var assign = make_node(AST_Assign, def, { operator: "=", left: make_node(AST_SymbolRef, def.name, def.name), right: def.value - }).transform(tt)); + }); + if (fixed_ids[sym.id] === def) { + fixed_ids[sym.id] = assign; + } + side_effects.push(assign.transform(tt)); } remove(var_defs, def); sym.eliminated++; return; } } - if (def.value && sym.id in fixed_ids && def.name.fixed_value() !== def.value) { - def.value = def.value.drop_side_effect_free(compressor); - } if (def.value) { if (side_effects.length > 0) { if (tail.length > 0) { @@ -3196,7 +3242,7 @@ merge(Compressor.prototype, { if (node instanceof AST_Assign) { node.right.walk(tw); if (node.left.fixed_value() === node.right) { - fixed_ids[node_def.id] = true; + fixed_ids[node_def.id] = node; } } return true; @@ -3648,23 +3694,28 @@ merge(Compressor.prototype, { }); function if_break_in_loop(self, compressor) { - function drop_it(rest) { - rest = as_statement_array(rest); - if (self.body instanceof AST_BlockStatement) { - self.body = self.body.clone(); - self.body.body = rest.concat(self.body.body.slice(1)); - self.body = self.body.transform(compressor); - } else { - self.body = make_node(AST_BlockStatement, self.body, { - body: rest - }).transform(compressor); - } - if_break_in_loop(self, compressor); - } var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body; + if (compressor.option("dead_code") && is_break(first)) { + var body = []; + if (self.init instanceof AST_Statement) { + body.push(self.init); + } else if (self.init) { + body.push(make_node(AST_SimpleStatement, self.init, { + body: self.init + })); + } + if (self.condition) { + body.push(make_node(AST_SimpleStatement, self.condition, { + body: self.condition + })); + } + extract_declarations_from_unreachable_code(compressor, self.body, body); + return make_node(AST_BlockStatement, self, { + body: body + }); + } if (first instanceof AST_If) { - if (first.body instanceof AST_Break - && compressor.loopcontrol_target(first.body) === compressor.self()) { + if (is_break(first.body)) { if (self.condition) { self.condition = make_node(AST_Binary, self.condition, { left: self.condition, @@ -3675,9 +3726,7 @@ merge(Compressor.prototype, { self.condition = first.condition.negate(compressor); } drop_it(first.alternative); - } - else if (first.alternative instanceof AST_Break - && compressor.loopcontrol_target(first.alternative) === compressor.self()) { + } else if (is_break(first.alternative)) { if (self.condition) { self.condition = make_node(AST_Binary, self.condition, { left: self.condition, @@ -3690,7 +3739,27 @@ merge(Compressor.prototype, { drop_it(first.body); } } - }; + return self; + + function is_break(node) { + return node instanceof AST_Break + && compressor.loopcontrol_target(node) === compressor.self(); + } + + function drop_it(rest) { + rest = as_statement_array(rest); + if (self.body instanceof AST_BlockStatement) { + self.body = self.body.clone(); + self.body.body = rest.concat(self.body.body.slice(1)); + self.body = self.body.transform(compressor); + } else { + self.body = make_node(AST_BlockStatement, self.body, { + body: rest + }).transform(compressor); + } + self = if_break_in_loop(self, compressor); + } + } OPT(AST_For, function(self, compressor){ if (!compressor.option("loops")) return self; @@ -3726,8 +3795,7 @@ merge(Compressor.prototype, { } } } - if_break_in_loop(self, compressor); - return self; + return if_break_in_loop(self, compressor); }); OPT(AST_If, function(self, compressor){ @@ -3983,9 +4051,20 @@ merge(Compressor.prototype, { OPT(AST_Try, function(self, compressor){ tighten_body(self.body, compressor); if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null; - if (all(self.body, is_empty)) { + if (compressor.option("dead_code") && all(self.body, is_empty)) { var body = []; - if (self.bcatch) extract_declarations_from_unreachable_code(compressor, self.bcatch, body); + if (self.bcatch) { + extract_declarations_from_unreachable_code(compressor, self.bcatch, body); + body.forEach(function(stat) { + if (!(stat instanceof AST_Definitions)) return; + stat.definitions.forEach(function(var_def) { + var def = var_def.name.definition().redefined(); + if (!def) return; + var_def.name = var_def.name.clone(); + var_def.name.thedef = def; + }); + }); + } if (self.bfinally) body = body.concat(self.bfinally.body); return make_node(AST_BlockStatement, self, { body: body @@ -5576,6 +5655,20 @@ merge(Compressor.prototype, { consequent ]).optimize(compressor); } + // x ? y || z : z --> x && y || z + if (consequent instanceof AST_Binary + && consequent.operator == "||" + && consequent.right.equivalent_to(alternative)) { + return make_node(AST_Binary, self, { + operator: "||", + left: make_node(AST_Binary, self, { + operator: "&&", + left: self.condition, + right: consequent.left + }), + right: alternative + }).optimize(compressor); + } var in_bool = compressor.in_boolean_context(); if (is_true(self.consequent)) { if (is_false(self.alternative)) { diff --git a/lib/output.js b/lib/output.js index b2353a4c..139075b3 100644 --- a/lib/output.js +++ b/lib/output.js @@ -476,79 +476,78 @@ function OutputStream(options) { var self = this; var start = node.start; if (!start) return; - if (!(start.comments_before && start.comments_before._dumped === self)) { - var comments = start.comments_before; - if (!comments) { - comments = start.comments_before = []; - } - comments._dumped = self; + if (start.comments_before && start.comments_before._dumped === self) return; + var comments = start.comments_before; + if (!comments) { + comments = start.comments_before = []; + } + comments._dumped = self; - if (node instanceof AST_Exit && node.value) { - var tw = new TreeWalker(function(node) { - var parent = tw.parent(); - if (parent instanceof AST_Exit - || parent instanceof AST_Binary && parent.left === node - || parent.TYPE == "Call" && parent.expression === node - || parent instanceof AST_Conditional && parent.condition === node - || parent instanceof AST_Dot && parent.expression === node - || parent instanceof AST_Sequence && parent.expressions[0] === node - || parent instanceof AST_Sub && parent.expression === node - || parent instanceof AST_UnaryPostfix) { - if (!node.start) return; - var text = node.start.comments_before; - if (text && text._dumped !== self) { - text._dumped = self; - comments = comments.concat(text); - } - } else { - return true; + if (node instanceof AST_Exit && node.value) { + var tw = new TreeWalker(function(node) { + var parent = tw.parent(); + if (parent instanceof AST_Exit + || parent instanceof AST_Binary && parent.left === node + || parent.TYPE == "Call" && parent.expression === node + || parent instanceof AST_Conditional && parent.condition === node + || parent instanceof AST_Dot && parent.expression === node + || parent instanceof AST_Sequence && parent.expressions[0] === node + || parent instanceof AST_Sub && parent.expression === node + || parent instanceof AST_UnaryPostfix) { + if (!node.start) return; + var text = node.start.comments_before; + if (text && text._dumped !== self) { + text._dumped = self; + comments = comments.concat(text); } - }); - tw.push(node); - node.value.walk(tw); - } - - if (current_pos == 0) { - if (comments.length > 0 && options.shebang && comments[0].type == "comment5") { - print("#!" + comments.shift().value + "\n"); - indent(); - } - var preamble = options.preamble; - if (preamble) { - print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); - } - } - - comments = comments.filter(comment_filter, node); - if (comments.length == 0) return; - var last_nlb = /(^|\n) *$/.test(OUTPUT); - comments.forEach(function(c, i) { - if (!last_nlb) { - if (c.nlb) { - print("\n"); - indent(); - last_nlb = true; - } else if (i > 0) { - space(); - } - } - if (/comment[134]/.test(c.type)) { - print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n"); - indent(); - last_nlb = true; - } else if (c.type == "comment2") { - print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); - last_nlb = false; + } else { + return true; } }); + tw.push(node); + node.value.walk(tw); + } + + if (current_pos == 0) { + if (comments.length > 0 && options.shebang && comments[0].type == "comment5") { + print("#!" + comments.shift().value + "\n"); + indent(); + } + var preamble = options.preamble; + if (preamble) { + print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); + } + } + + comments = comments.filter(comment_filter, node); + if (comments.length == 0) return; + var last_nlb = /(^|\n) *$/.test(OUTPUT); + comments.forEach(function(c, i) { if (!last_nlb) { - if (start.nlb) { + if (c.nlb) { print("\n"); indent(); - } else { + last_nlb = true; + } else if (i > 0) { space(); } } + if (/comment[134]/.test(c.type)) { + print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n"); + indent(); + last_nlb = true; + } else if (c.type == "comment2") { + print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); + last_nlb = false; + } + }); + if (!last_nlb) { + if (start.nlb) { + print("\n"); + indent(); + } else { + space(); + } } } @@ -557,35 +556,33 @@ function OutputStream(options) { var token = node.end; if (!token) return; var comments = token[tail ? "comments_before" : "comments_after"]; - if (comments - && comments._dumped !== self - && (node instanceof AST_Statement || all(comments, function(c) { - return !/comment[134]/.test(c.type); - }))) { - comments._dumped = self; - var insert = OUTPUT.length; - comments.filter(comment_filter, node).forEach(function(c, i) { - need_space = false; - if (need_newline_indented) { - print("\n"); - indent(); - need_newline_indented = false; - } else if (c.nlb && (i > 0 || !/(^|\n) *$/.test(OUTPUT))) { - print("\n"); - indent(); - } else if (i > 0 || !tail) { - space(); - } - if (/comment[134]/.test(c.type)) { - print("//" + c.value.replace(/[@#]__PURE__/g, ' ')); - need_newline_indented = true; - } else if (c.type == "comment2") { - print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); - need_space = true; - } - }); - if (OUTPUT.length > insert) newline_insert = insert; - } + if (!comments || comments._dumped === self) return; + if (!(node instanceof AST_Statement || all(comments, function(c) { + return !/comment[134]/.test(c.type); + }))) return; + comments._dumped = self; + var insert = OUTPUT.length; + comments.filter(comment_filter, node).forEach(function(c, i) { + need_space = false; + if (need_newline_indented) { + print("\n"); + indent(); + need_newline_indented = false; + } else if (c.nlb && (i > 0 || !/(^|\n) *$/.test(OUTPUT))) { + print("\n"); + indent(); + } else if (i > 0 || !tail) { + space(); + } + if (/comment[134]/.test(c.type)) { + print("//" + c.value.replace(/[@#]__PURE__/g, ' ')); + need_newline_indented = true; + } else if (c.type == "comment2") { + print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); + need_space = true; + } + }); + if (OUTPUT.length > insert) newline_insert = insert; } var stack = []; diff --git a/lib/scope.js b/lib/scope.js index a19853ee..a118aa8b 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -58,7 +58,7 @@ function SymbolDef(scope, orig, init) { this.id = SymbolDef.next_id++; }; -SymbolDef.next_id = 1; +SymbolDef.next_id = 1e6; SymbolDef.prototype = { unmangleable: function(options) { @@ -576,59 +576,55 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){ } }); -AST_Toplevel.DEFMETHOD("find_unique_prefix", function(options) { - var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"; +AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { var cache = options.cache && options.cache.props; - var prefixes = Object.create(null); - options.reserved.forEach(add_prefix); + var avoid = Object.create(null); + options.reserved.forEach(to_avoid); this.globals.each(add_def); this.walk(new TreeWalker(function(node) { if (node instanceof AST_Scope) node.variables.each(add_def); if (node instanceof AST_SymbolCatch) add_def(node.definition()); })); - var prefix, i = 0; - do { - prefix = create_name(i++); - } while (prefixes[prefix]); - return prefix; + return avoid; - function add_prefix(name) { - if (/[0-9]$/.test(name)) { - prefixes[name.replace(/[0-9]+$/, "")] = true; - } + function to_avoid(name) { + avoid[name] = true; } function add_def(def) { var name = def.name; if (def.global && cache && cache.has(name)) name = cache.get(name); else if (!def.unmangleable(options)) return; - add_prefix(name); - } - - function create_name(num) { - var name = ""; - do { - name += letters[num % letters.length]; - num = Math.floor(num / letters.length); - } while (num); - return name; + to_avoid(name); } }); AST_Toplevel.DEFMETHOD("expand_names", function(options) { + base54.reset(); + base54.sort(); options = this._default_mangler_options(options); - var prefix = this.find_unique_prefix(options); + var avoid = this.find_colliding_names(options); + var cname = 0; this.globals.each(rename); this.walk(new TreeWalker(function(node) { if (node instanceof AST_Scope) node.variables.each(rename); if (node instanceof AST_SymbolCatch) rename(node.definition()); })); + function next_name() { + var name; + do { + name = base54(cname++); + } while (avoid[name] || !is_identifier(name)); + return name; + } + function rename(def) { - if (def.global || def.unmangleable(options)) return; + if (def.global && options.cache) return; + if (def.unmangleable(options)) return; if (member(def.name, options.reserved)) return; var d = def.redefined(); - def.name = d ? d.name : prefix + def.id; + def.name = d ? d.name : next_name(); def.orig.forEach(function(sym) { sym.name = def.name; }); diff --git a/package.json b/package.json index 1902590e..231f2333 100644 --- a/package.json +++ b/package.json @@ -4,17 +4,15 @@ "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.3.5", + "version": "3.3.6", "engines": { "node": ">=0.8.0" }, "maintainers": [ + "Alex Lam ", "Mihai Bazon (http://lisperator.net/)" ], - "repository": "git+https://github.com/mishoo/UglifyJS2.git#harmony", - "bugs": { - "url": "https://github.com/mishoo/UglifyJS2/issues" - }, + "repository": "https://github.com/mishoo/UglifyJS2.git#harmony", "main": "tools/node.js", "bin": { "uglifyjs": "bin/uglifyjs" @@ -26,11 +24,11 @@ "LICENSE" ], "dependencies": { - "commander": "~2.12.1", + "commander": "~2.13.0", "source-map": "~0.6.1" }, "devDependencies": { - "acorn": "~5.2.1", + "acorn": "~5.3.0", "mocha": "~3.5.1", "semver": "~5.4.1" }, diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index a819ac95..ed3da314 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -2296,6 +2296,7 @@ toplevel_single_reference: { unused_orig: { options = { collapse_vars: true, + dead_code: true, passes: 2, reduce_funcs: true, reduce_vars: true, diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js index 7838fdbb..e37c5556 100644 --- a/test/compress/conditionals.js +++ b/test/compress/conditionals.js @@ -1224,3 +1224,46 @@ hoist_decl: { x() ? y() : z(); } } + +to_and_or: { + options = { + conditionals: true, + } + input: { + var values = [ + 0, + null, + true, + "foo", + false, + -1 / 0, + void 0, + ]; + values.forEach(function(x) { + values.forEach(function(y) { + values.forEach(function(z) { + console.log(x ? y || z : z); + }); + }); + }); + } + expect: { + var values = [ + 0, + null, + true, + "foo", + false, + -1 / 0, + void 0, + ]; + values.forEach(function(x) { + values.forEach(function(y) { + values.forEach(function(z) { + console.log(x && y || z); + }); + }); + }); + } + expect_stdout: true +} diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index eb213b95..17aadba7 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -1128,3 +1128,32 @@ issue_2701: { } expect_stdout: "function" } + +issue_2749: { + options = { + dead_code: true, + inline: true, + toplevel: true, + unused: true, + } + input: { + var a = 2, c = "PASS"; + while (a--) + (function() { + return b ? c = "FAIL" : b = 1; + try { + } catch (b) { + var b; + } + })(); + console.log(c); + } + expect: { + var a = 2, c = "PASS"; + while (a--) + b = void 0, b ? c = "FAIL" : b = 1; + var b; + console.log(c); + } + expect_stdout: "PASS" +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 9101c38f..0324d722 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -2018,3 +2018,51 @@ cascade_drop_assign: { } expect_stdout: "PASS" } + +chained_3: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a, b) { + var c = a, c = b; + b++; + return c; + }(1, 2)); + } + expect: { + console.log(function(a, b) { + var c = b; + b++; + return c; + }(0, 2)); + } + expect_stdout: "2" +} + +issue_2768: { + options = { + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL", c = 1; + var c = function(b) { + var d = b = a; + var e = --b + (d && (a = "PASS")); + }(); + console.log(a, typeof c); + } + expect: { + var a = "FAIL"; + var c = (d = a, 0, void (d && (a = "PASS"))); + var d; + console.log(a, typeof c); + } + expect_stdout: "PASS undefined" +} diff --git a/test/compress/loops.js b/test/compress/loops.js index c5934f6f..213a107f 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -536,3 +536,116 @@ dead_code_condition: { } expect_stdout: "1" } + +issue_2740_1: { + options = { + dead_code: true, + loops: true, + } + input: { + for (; ; ) break; + for (a(); ; ) break; + for (; b(); ) break; + for (c(); d(); ) break; + for (; ; e()) break; + for (f(); ; g()) break; + for (; h(); i()) break; + for (j(); k(); l()) break; + } + expect: { + a(); + b(); + c(); + d(); + f(); + h(); + j(); + k(); + } +} + +issue_2740_2: { + options = { + dead_code: true, + loops: true, + passes: 2, + } + input: { + L1: while (x()) { + break L1; + } + } + expect: { + x(); + } +} + +issue_2740_3: { + options = { + dead_code: true, + loops: true, + } + input: { + L1: for (var x = 0; x < 3; x++) { + L2: for (var y = 0; y < 2; y++) { + break L1; + } + } + console.log(x, y); + } + expect: { + L1: for (var x = 0; x < 3; x++) + for (var y = 0; y < 2; y++) + break L1; + console.log(x, y); + } + expect_stdout: "0 0" +} + +issue_2740_4: { + options = { + dead_code: true, + loops: true, + passes: 2, + } + input: { + L1: for (var x = 0; x < 3; x++) { + L2: for (var y = 0; y < 2; y++) { + break L2; + } + } + console.log(x, y); + } + expect: { + for (var x = 0; x < 3; x++) { + var y = 0; + y < 2; + } + console.log(x, y); + } + expect_stdout: "3 0" +} + +issue_2740_5: { + options = { + dead_code: true, + loops: true, + passes: 2, + } + input: { + L1: for (var x = 0; x < 3; x++) { + break L1; + L2: for (var y = 0; y < 2; y++) { + break L2; + } + } + console.log(x, y); + } + expect: { + var x = 0; + x < 3; + var y; + console.log(x,y); + } + expect_stdout: "0 undefined" +} diff --git a/test/compress/properties.js b/test/compress/properties.js index 0d50cdc3..a17fe690 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -1377,3 +1377,91 @@ const_prop_assign_pure: { x(); } } + +join_object_assignments_1: { + options = { + evaluate: true, + join_vars: true, + } + input: { + console.log(function() { + var x = { + a: 1, + c: (console.log("c"), "C"), + }; + x.b = 2; + x[3] = function() { + console.log(x); + }, + x["a"] = /foo/, + x.bar = x; + return x; + }()); + } + expect: { + console.log(function() { + var x = { + a: 1, + c: (console.log("c"), "C"), + b: 2, + 3: function() { + console.log(x); + }, + a: /foo/, + }; + x.bar = x; + return x; + }()); + } + expect_stdout: true +} + +join_object_assignments_2: { + options = { + evaluate: true, + hoist_props: true, + join_vars: true, + passes: 3, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + foo: 1, + }; + o.bar = 2; + o.baz = 3; + console.log(o.foo, o.bar + o.bar, o.foo * o.bar * o.baz); + } + expect: { + console.log(1, 4, 6); + } + expect_stdout: "1 4 6" +} + +join_object_assignments_3: { + options = { + evaluate: true, + join_vars: true, + } + input: { + console.log(function() { + var o = { + a: "PASS", + }, a = o.a; + o.a = "FAIL"; + return a; + }()); + } + expect: { + console.log(function() { + var o = { + a: "PASS", + }, a = o.a; + o.a = "FAIL"; + return a; + }()); + } + expect_stdout: "PASS" +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 6d711083..1d94e989 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -5866,3 +5866,29 @@ issue_2757_2: { ] node_version: ">=6" } + +issue_2774: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log({ + get a() { + var b; + (b = true) && b.c; + b = void 0; + } + }.a); + } + expect: { + console.log({ + get a() { + var b; + (b = true) && b.c; + b = void 0; + } + }.a); + } + expect_stdout: "undefined" +} diff --git a/test/mocha/minify.js b/test/mocha/minify.js index e68ab2a2..b582c4d6 100644 --- a/test/mocha/minify.js +++ b/test/mocha/minify.js @@ -404,4 +404,18 @@ describe("minify", function() { assert.strictEqual(stat.print_to_string(), "a=x()"); }); }); + + describe("rename", function() { + it("Should be repeatable", function() { + var code = "!function(x){return x(x)}(y);"; + for (var i = 0; i < 2; i++) { + assert.strictEqual(Uglify.minify(code, { + compress: { + toplevel: true, + }, + rename: true, + }).code, "var a;(a=y)(a);"); + } + }); + }); });