diff --git a/lib/compress.js b/lib/compress.js index 1342997f..d2e1e939 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -126,15 +126,13 @@ function Compressor(options, false_by_default) { }; } var toplevel = this.options["toplevel"]; - if (typeof toplevel == "string") { - this.toplevel.funcs = /funcs/.test(toplevel); - this.toplevel.vars = /vars/.test(toplevel); - } else { - this.toplevel = toplevel ? function(def) { - return !def.export; - } : return_false; - this.toplevel.funcs = this.toplevel.vars = toplevel; - } + this.toplevel = typeof toplevel == "string" ? { + funcs: /funcs/.test(toplevel), + vars: /vars/.test(toplevel) + } : { + funcs: toplevel, + vars: toplevel + }; var sequences = this.options["sequences"]; this.sequences_limit = sequences == 1 ? 800 : sequences | 0; this.warnings_produced = {}; @@ -143,12 +141,12 @@ function Compressor(options, false_by_default) { Compressor.prototype = new TreeTransformer; merge(Compressor.prototype, { option: function(key) { return this.options[key] }, - toplevel: function(def) { - if (def.export) return false; - for (var i = 0, len = def.orig.length; i < len; i++) + exposed: function(def) { + if (def.export) return true; + if (def.global) for (var i = 0, len = def.orig.length; i < len; i++) if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"]) - return false; - return true; + return true; + return false; }, compress: function(node) { if (this.option("expression")) { @@ -284,11 +282,11 @@ merge(Compressor.prototype, { var reduce_vars = rescan && compressor.option("reduce_vars"); var safe_ids = Object.create(null); var suppressor = new TreeWalker(function(node) { - if (node instanceof AST_Symbol) { - var d = node.definition(); - if (node instanceof AST_SymbolRef) d.references.push(node); - d.fixed = false; - } + if (!(node instanceof AST_Symbol)) return; + var d = node.definition(); + if (!d) return; + if (node instanceof AST_SymbolRef) d.references.push(node); + d.fixed = false; }); var tw = new TreeWalker(function(node, descend) { node._squeezed = false; @@ -356,7 +354,7 @@ merge(Compressor.prototype, { } if (node instanceof AST_Defun) { var d = node.name.definition(); - if (d.global && !compressor.toplevel(d) || safe_to_read(d)) { + if (compressor.exposed(d) || safe_to_read(d)) { d.fixed = false; } else { d.fixed = node; @@ -531,7 +529,7 @@ merge(Compressor.prototype, { def.escaped = false; if (def.scope.uses_eval) { def.fixed = false; - } else if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) { + } else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) { def.fixed = undefined; } else { def.fixed = false; @@ -561,8 +559,25 @@ merge(Compressor.prototype, { return fixed(); }); + AST_SymbolRef.DEFMETHOD("is_immutable", function() { + var orig = this.definition().orig; + return orig.length == 1 && orig[0] instanceof AST_SymbolLambda; + }); + function is_lhs_read_only(lhs) { - return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda; + if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda; + if (lhs instanceof AST_PropAccess) { + lhs = lhs.expression; + if (lhs instanceof AST_SymbolRef) { + if (lhs.is_immutable()) return false; + lhs = lhs.fixed_value(); + } + if (!lhs) return true; + if (lhs instanceof AST_RegExp) return false; + if (lhs instanceof AST_Constant) return true; + return is_lhs_read_only(lhs); + } + return false; } function is_ref_of(ref, type) { @@ -783,7 +798,7 @@ merge(Compressor.prototype, { } if (candidate instanceof AST_VarDef) { var def = candidate.name.definition(); - if (def.references.length == 1 && (!def.global || compressor.toplevel(def))) { + if (def.references.length == 1 && !compressor.exposed(def)) { return maintain_this_binding(parent, node, candidate.value); } return make_node(AST_Assign, candidate, { @@ -845,7 +860,7 @@ merge(Compressor.prototype, { if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) { var def = expr.name.definition(); if (def.orig.length > 1 - || def.references.length == 1 && (!def.global || compressor.toplevel(def))) { + || def.references.length == 1 && !compressor.exposed(def)) { return make_node(AST_SymbolRef, expr.name, expr.name); } } else { @@ -1334,6 +1349,7 @@ merge(Compressor.prototype, { def(AST_SymbolRef, function(pure_getters) { if (this.is_undefined) return true; if (!is_strict(pure_getters)) return false; + if (this.is_immutable()) return false; var fixed = this.fixed_value(); return !fixed || fixed._throw_on_access(pure_getters); }); @@ -3270,7 +3286,9 @@ merge(Compressor.prototype, { var comp = new Compressor(compressor.options); ast = ast.transform(comp); ast.figure_out_scope(mangle); - ast.mangle_names(); + base54.reset(); + ast.compute_char_frequency(mangle); + ast.mangle_names(mangle); var fun; ast.walk(new TreeWalker(function(node) { if (fun) return true; @@ -3525,6 +3543,8 @@ merge(Compressor.prototype, { || cdr instanceof AST_PropAccess || cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) { field = "expression"; + } else if (cdr instanceof AST_Conditional) { + field = "condition"; } else { expressions[++i] = expressions[j]; break; @@ -4059,7 +4079,7 @@ merge(Compressor.prototype, { var d = self.definition(); var fixed = self.fixed_value(); if (fixed instanceof AST_Defun) { - d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true); + d.fixed = fixed = make_node(AST_Function, fixed, fixed); } if (compressor.option("unused") && fixed instanceof AST_Function @@ -4067,7 +4087,7 @@ merge(Compressor.prototype, { && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) && !d.scope.uses_eval && compressor.find_parent(AST_Scope) === fixed.parent_scope) { - return fixed; + return fixed.clone(true); } if (compressor.option("evaluate") && fixed) { if (d.should_replace === undefined) { @@ -4090,7 +4110,7 @@ merge(Compressor.prototype, { } var name_length = d.name.length; var overhead = 0; - if (compressor.option("unused") && (!d.global || compressor.toplevel(d))) { + if (compressor.option("unused") && !compressor.exposed(d)) { overhead = (name_length + 2 + value_length) / d.references.length; } d.should_replace = value_length <= name_length + overhead ? fn : false; diff --git a/lib/output.js b/lib/output.js index 3f9dd78b..06ff762f 100644 --- a/lib/output.js +++ b/lib/output.js @@ -533,6 +533,7 @@ function OutputStream(options) { use_asm = prev_use_asm; } }); + AST_Node.DEFMETHOD("_print", AST_Node.prototype.print); AST_Node.DEFMETHOD("print_to_string", function(options){ var s = OutputStream(options); diff --git a/lib/parse.js b/lib/parse.js index a96bb150..04a6502b 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1230,9 +1230,12 @@ function parse($TEXT, options) { var is_in = is("operator", "in"); var is_of = is("name", "of"); if (is_in || is_of) { - if ((init instanceof AST_Definitions) && - init.definitions.length > 1) - croak("Only one variable declaration allowed in for..in loop"); + if (init instanceof AST_Definitions) { + if (init.definitions.length > 1) + croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos); + } else if (!(is_assignable(init) || (init = to_destructuring(init)) instanceof AST_Destructuring)) { + croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos); + } next(); if (is_in) { return for_in(init); diff --git a/lib/scope.js b/lib/scope.js index 33890c66..ed0f0a37 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -450,7 +450,7 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){ AST_Symbol.DEFMETHOD("unmangleable", function(options){ var def = this.definition(); - return def && def.unmangleable(options); + return !def || def.unmangleable(options); }); // labels are always mangleable @@ -555,113 +555,69 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ options = this._default_mangler_options(options); - var tw = new TreeWalker(function(node){ - if (node instanceof AST_Constant) - base54.consider(node.print_to_string()); - else if (node instanceof AST_Return) - base54.consider("return"); - else if (node instanceof AST_Throw) - base54.consider("throw"); - else if (node instanceof AST_Continue) - base54.consider("continue"); - else if (node instanceof AST_Break) - base54.consider("break"); - else if (node instanceof AST_Debugger) - base54.consider("debugger"); - else if (node instanceof AST_Directive) - base54.consider(node.value); - else if (node instanceof AST_While) - base54.consider("while"); - else if (node instanceof AST_Do) - base54.consider("do while"); - else if (node instanceof AST_If) { - base54.consider("if"); - if (node.alternative) base54.consider("else"); - } - else if (node instanceof AST_Var) - base54.consider("var"); - else if (node instanceof AST_Const) - base54.consider("const"); - else if (node instanceof AST_Lambda) - base54.consider("function"); - else if (node instanceof AST_For) - base54.consider("for"); - else if (node instanceof AST_ForIn) - base54.consider("for in"); - else if (node instanceof AST_Switch) - base54.consider("switch"); - else if (node instanceof AST_Case) - base54.consider("case"); - else if (node instanceof AST_Default) - base54.consider("default"); - else if (node instanceof AST_With) - base54.consider("with"); - else if (node instanceof AST_ObjectSetter) - base54.consider("set" + (typeof node.key === "string" ? node.key : "")); - else if (node instanceof AST_ObjectGetter) - base54.consider("get" + (typeof node.key === "string" ? node.key : "")); - else if (node instanceof AST_ObjectKeyVal && typeof node.key === "string") - base54.consider(node.key); - else if (node instanceof AST_ConciseMethod && typeof node.key === "string") - base54.consider(node.key); - else if (node instanceof AST_New) - base54.consider("new"); - else if (node instanceof AST_This) - base54.consider("this"); - else if (node instanceof AST_Super) - base54.consider("super"); - else if (node instanceof AST_Try) - base54.consider("try"); - else if (node instanceof AST_Catch) - base54.consider("catch"); - else if (node instanceof AST_Finally) - base54.consider("finally"); - else if (node instanceof AST_Yield) - base54.consider("yield"); - else if (node instanceof AST_Await) - base54.consider("await"); - else if (node instanceof AST_Symbol && node.unmangleable(options)) - base54.consider(node.name); - else if (node instanceof AST_Unary || node instanceof AST_Binary) - base54.consider(node.operator); - else if (node instanceof AST_Dot) - base54.consider(node.property); - }); - this.walk(tw); + try { + AST_Node.prototype.print = function(stream, force_parens) { + this._print(stream, force_parens); + if (this instanceof AST_Symbol && !this.unmangleable(options)) { + base54.consider(this.name, -1); + } else if (options.properties) { + if (this instanceof AST_Dot) { + base54.consider(this.property, -1); + } else if (this instanceof AST_Sub) { + skip_string(this.property); + } + } + }; + base54.consider(this.print_to_string(), 1); + } finally { + AST_Node.prototype.print = AST_Node.prototype._print; + } base54.sort(); + + function skip_string(node) { + if (node instanceof AST_String) { + base54.consider(node.value, -1); + } else if (node instanceof AST_Conditional) { + skip_string(node.consequent); + skip_string(node.alternative); + } else if (node instanceof AST_Sequence) { + skip_string(node.expressions[node.expressions.length - 1]); + } + } }); var base54 = (function() { - var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; + var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split(""); + var digits = "0123456789".split(""); var chars, frequency; function reset() { frequency = Object.create(null); - chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); - chars.forEach(function(ch){ frequency[ch] = 0 }); + leading.forEach(function(ch) { + frequency[ch] = 0; + }); + digits.forEach(function(ch) { + frequency[ch] = 0; + }); } - base54.consider = function(str){ + base54.consider = function(str, delta) { for (var i = str.length; --i >= 0;) { - var code = str.charCodeAt(i); - if (code in frequency) ++frequency[code]; + frequency[str[i]] += delta; } }; + function compare(a, b) { + return frequency[b] - frequency[a]; + } base54.sort = function() { - chars = mergeSort(chars, function(a, b){ - if (is_digit(a) && !is_digit(b)) return 1; - if (is_digit(b) && !is_digit(a)) return -1; - return frequency[b] - frequency[a]; - }); + chars = mergeSort(leading, compare).concat(mergeSort(digits, compare)); }; base54.reset = reset; reset(); - base54.get = function(){ return chars }; - base54.freq = function(){ return frequency }; function base54(num) { var ret = "", base = 54; num++; do { num--; - ret += String.fromCharCode(chars[num % base]); + ret += chars[num % base]; num = Math.floor(num / base); base = 64; } while (num > 0); diff --git a/package.json b/package.json index 2ee37f70..f0e10827 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.0.19", + "version": "3.0.20", "engines": { "node": ">=0.8.0" }, diff --git a/test/compress/block-scope.js b/test/compress/block-scope.js index f16d34c4..6984f62e 100644 --- a/test/compress/block-scope.js +++ b/test/compress/block-scope.js @@ -104,34 +104,36 @@ regression_block_scope_resolves: { }; input: { (function () { - if(1) { + if (1) { let x; - const y; + const y = 1; class Zee {}; } - if(1) { + if (1) { let ex; - const why; + const why = 2; class Zi {}; } - console.log(x, y, Zee, ex, why, Zi); + console.log(typeof x, typeof y, typeof Zee, typeof ex, typeof why, typeof Zi); }()); } expect: { (function () { if (1) { - let o; - const n; - class c {}; + let e; + const o = 1; + class t {}; } if (1) { - let o; - const n; - class c {}; + let e; + const o = 2; + class t {}; } - console.log(x, y, Zee, ex, why, Zi); + console.log(typeof x, typeof y, typeof Zee, typeof ex, typeof why, typeof Zi); }()); } + expect_stdout: "undefined undefined undefined undefined undefined undefined" + node_version: ">=6" } switch_block_scope_mangler: { @@ -153,25 +155,37 @@ switch_block_scope_mangler: { console.log(cat); } }; + fn(1); + fn(2); + fn(3); } expect: { - var fn = function(o) { - switch (o) { + var fn = function(e) { + switch (e) { case 1: - let e = o + 1 - let c = o + 4; - console.log(e, c); + let l = e + 1 + let o = e + 4; + console.log(l, o); break; case 2: - let l = o + 2; - console.log(l); + let n = e + 2; + console.log(n); break; default: - let a = o + 3; - console.log(a); + let c = e + 3; + console.log(c); } }; + fn(1); + fn(2); + fn(3); } + expect_stdout: [ + "2 5", + "4", + "6", + ] + node_version: ">=6" } diff --git a/test/compress/destructuring.js b/test/compress/destructuring.js index 56adf266..0c85e432 100644 --- a/test/compress/destructuring.js +++ b/test/compress/destructuring.js @@ -425,8 +425,8 @@ mangle_destructuring_decl: { expect: { function test(t) { let e = t.a || { e: 7, n: 8 }; - let {t: n, e: o, n: s, s: a = 9, o: c, r: l} = e; - console.log(n, o, s, a, c, l); + let {t: n, e: o, n: s, s: l = 9, o: a, r: c} = e; + console.log(n, o, s, l, a, c); } test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } }); test({}); @@ -462,15 +462,15 @@ mangle_destructuring_assign_toplevel_true: { test({}); } expect: { - function n(n) { - let t, a, c; - let l = n.a || { e: 7, n: 8 }; - ({t: o, e, n: s, s: t = 9, o: a, r: c} = l); - console.log(o, e, s, t, a, c); + function e(e) { + let l, s, a; + let c = e.a || { e: 7, n: 8 }; + ({t: n, e: o, n: t, s: l = 9, o: s, r: a} = c); + console.log(n, o, t, l, s, a); } - let o, e, s; - n({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } }); - n({}); + let n, o, t; + e({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } }); + e({}); } expect_stdout: [ "1 2 3 4 5 6", @@ -504,10 +504,10 @@ mangle_destructuring_assign_toplevel_false: { } expect: { function test(o) { - let s, a, c; - let l = o.a || { e: 7, n: 8 }; - ({t, e, n, s = 9, o: a, r: c} = l); - console.log(t, e, n, s, a, c); + let s, l, a; + let c = o.a || { e: 7, n: 8 }; + ({t, e, n, s = 9, o: l, r: a} = c); + console.log(t, e, n, s, l, a); } let t, e, n; test({ a: { t: 1, e: 2, n: 3, s: 4, o: 5, r: 6 } }); @@ -588,8 +588,8 @@ arrow_func_with_destructuring_args: { })({bar: 5 - 0}, [, 6]); } expect: { - (({foo: o = 1, bar: n = 2}, [a = 3, b = 4]) => { - console.log(o, n, a, b); + (({foo: o = 1, bar: a = 2}, [b = 3, l = 4]) => { + console.log(o, a, b, l); })({bar: 5}, [, 6]); } expect_stdout: "1 5 3 6" diff --git a/test/compress/export.js b/test/compress/export.js index f20ed208..84c0e5b4 100644 --- a/test/compress/export.js +++ b/test/compress/export.js @@ -33,10 +33,10 @@ issue_2038_2: { export { LET, CONST, VAR }; } expect: { - let a = 1; - const c = 2; - var n = 3; - export { a as LET, c as CONST, n as VAR }; + let t = 1; + const e = 2; + var o = 3; + export { t as LET, e as CONST, o as VAR }; } } @@ -51,10 +51,10 @@ issue_2126: { export { dog }; } expect: { - import { foo as o, cat as f } from "stuff"; - console.log(o, f); + import { foo as o, cat as s } from "stuff"; + console.log(o, s); export { o as qux }; - export { f as dog }; + export { s as dog }; } } @@ -166,9 +166,9 @@ redirection: { export { foo as var } from "module.js"; } expect: { - let o = 1, d = 2; - export { o as delete }; - export { d as default }; + let e = 1, o = 2; + export { e as delete }; + export { o as default }; export { foo as var } from "module.js"; } } diff --git a/test/compress/functions.js b/test/compress/functions.js index c6de3276..065d54db 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -265,7 +265,7 @@ issue_203: { } expect: { var m = {}; - var fn = Function("a", "b", "b.exports=42"); + var fn = Function("n", "o", "o.exports=42"); fn(null, m, m.exports); console.log(m.exports); } diff --git a/test/compress/harmony.js b/test/compress/harmony.js index 9d36f429..9b7e6aa0 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -63,15 +63,15 @@ class_name_can_be_mangled: { function x() { class Foo { } - var class1 = Foo - var class2 = class Bar {} + var class1 = Foo; + var class2 = class Bar {}; } } expect: { function x() { class a { } - var n = a - var r = class a {} + var s = a; + var c = class a {}; } } } @@ -290,12 +290,12 @@ import_statement_mangling: { Whatever(); } expect: { - import l from "foo"; - import e, {Food as o} from "lel"; + import o from "foo"; + import m, {Food as r} from "lel"; import {What as f} from "lel"; - l(); - e(); o(); + m(); + r(); f(); } } @@ -469,10 +469,10 @@ issue_1898: { expect: { class Foo { bar() { - for (const n of [ 6, 5 ]) + for (const f of [ 6, 5 ]) for (let r of [ 4, 3 ]) for (var o of [ 2, 1 ]) - console.log(n, r, o); + console.log(f, r, o); } } new Foo().bar(); @@ -497,9 +497,9 @@ issue_1753: { expect: { class SomeClass { constructor(r) { - let a = []; - for (let s = 0; s < 6; s++) - a.push({ + let s = []; + for (let a = 0; a < 6; a++) + s.push({ mainDrawNumbers: [], extraDrawNumbers: [] }); @@ -526,9 +526,9 @@ issue_1753_disable: { expect: { class SomeClass { constructor(r) { - let a = []; + let s = []; for (let r = 0; r < 6; r++) - a.push({ + s.push({ mainDrawNumbers: [], extraDrawNumbers: [] }); diff --git a/test/compress/issue-1321.js b/test/compress/issue-1321.js index dcbfde64..6b92291d 100644 --- a/test/compress/issue-1321.js +++ b/test/compress/issue-1321.js @@ -10,9 +10,9 @@ issue_1321_no_debug: { } expect: { var x = {}; - x.b = 1; - x["a"] = 2 * x.b; - console.log(x.b, x["a"]); + x.o = 1; + x["a"] = 2 * x.o; + console.log(x.o, x["a"]); } expect_stdout: true } @@ -30,9 +30,9 @@ issue_1321_debug: { } expect: { var x = {}; - x.a = 1; - x["_$foo$_"] = 2 * x.a; - console.log(x.a, x["_$foo$_"]); + x.o = 1; + x["_$foo$_"] = 2 * x.o; + console.log(x.o, x["_$foo$_"]); } expect_stdout: true } @@ -49,9 +49,9 @@ issue_1321_with_quoted: { } expect: { var x = {}; - x.a = 1; - x["b"] = 2 * x.a; - console.log(x.a, x["b"]); + x.o = 1; + x["x"] = 2 * x.o; + console.log(x.o, x["x"]); } expect_stdout: true } diff --git a/test/compress/issue-1466.js b/test/compress/issue-1466.js index 9273235b..5827aee8 100644 --- a/test/compress/issue-1466.js +++ b/test/compress/issue-1466.js @@ -33,8 +33,8 @@ same_variable_in_multiple_for_loop: { console.log(o, l); for (let o = 0; o < 2; o++) { console.log(o, l); - let c = 2; - console.log(c); + let e = 2; + console.log(e); } } } @@ -114,12 +114,12 @@ same_variable_in_multiple_forIn: { } expect: { var test = [ "a", "b", "c" ]; - for (let o in test) { - console.log(o); - let e; - e = [ "e", "f", "g" ]; - for (let o in test) - console.log(o); + for (let e in test) { + console.log(e); + let t; + t = [ "e", "f", "g" ]; + for (let e in test) + console.log(e); } } expect_stdout: true @@ -160,8 +160,8 @@ different_variable_in_multiple_for_loop: { console.log(o, l); for (let o = 0; o < 2; o++) { console.log(o, l); - let c = 2; - console.log(c); + let e = 2; + console.log(e); } } } @@ -241,12 +241,12 @@ different_variable_in_multiple_forIn: { } expect: { var test = [ "a", "b", "c" ]; - for (let o in test) { - console.log(o); - let e; - e = [ "e", "f", "g" ]; - for (let o in test) - console.log(o); + for (let e in test) { + console.log(e); + let t; + t = [ "e", "f", "g" ]; + for (let e in test) + console.log(e); } } expect_stdout: true @@ -281,10 +281,10 @@ more_variable_in_multiple_for: { } expect: { for (let o = 9, l = 0; l < 20; l += o) { - let c = o++ + l; - console.log(o, c, l); - for (let l = c, e = c * c, f = 0; f < 10; f++) - console.log(o, c, e, l, f); + let e = o++ + l; + console.log(o, e, l); + for (let l = e, t = e * e, c = 0; c < 10; c++) + console.log(o, e, t, l, c); } } expect_stdout: true diff --git a/test/compress/issue-1770.js b/test/compress/issue-1770.js index de0d4dd3..34e4dc75 100644 --- a/test/compress/issue-1770.js +++ b/test/compress/issue-1770.js @@ -82,7 +82,7 @@ numeric_literal: { ' 42: 2,', ' "42": 3,', ' 37: 4,', - ' a: 5,', + ' o: 5,', ' 1e42: 6,', ' b: 7,', ' "1e+42": 8', @@ -92,7 +92,7 @@ numeric_literal: { '', 'console.log(obj[42], obj["42"]);', '', - 'console.log(obj[37], obj["a"], obj[37], obj["37"]);', + 'console.log(obj[37], obj["o"], obj[37], obj["37"]);', '', 'console.log(obj[1e42], obj["b"], obj["1e+42"]);', ] diff --git a/test/compress/issue-2001.js b/test/compress/issue-2001.js index 870bc1ec..3938072a 100644 --- a/test/compress/issue-2001.js +++ b/test/compress/issue-2001.js @@ -159,7 +159,7 @@ export_mangle_1: { return one - two; }; } - expect_exact: "export function foo(n,o){return n-o};" + expect_exact: "export function foo(o,n){return o-n};" } export_mangle_2: { @@ -171,7 +171,7 @@ export_mangle_2: { return one - two; }; } - expect_exact: "export default function foo(n,o){return n-o};" + expect_exact: "export default function foo(o,t){return o-t};" } export_mangle_3: { @@ -189,7 +189,7 @@ export_mangle_3: { } }; } - expect_exact: "export class C{go(n,r){return n-r+n}};" + expect_exact: "export class C{go(r,e){return r-e+r}};" } export_mangle_4: { @@ -207,7 +207,7 @@ export_mangle_4: { } }; } - expect_exact: "export default class C{go(n,r){return n-r+n}};" + expect_exact: "export default class C{go(e,r){return e-r+e}};" } export_mangle_5: { @@ -221,7 +221,7 @@ export_mangle_5: { } }; } - expect_exact: "export default{prop:function(n,r){return n-r}};" + expect_exact: "export default{prop:function(r,t){return r-t}};" } export_mangle_6: { @@ -232,7 +232,7 @@ export_mangle_6: { var baz = 2; export let foo = 1, bar = baz; } - expect_exact: "var a=2;export let foo=1,bar=a;" + expect_exact: "var o=2;export let foo=1,bar=o;" } export_toplevel_1: { diff --git a/test/compress/issue-203.js b/test/compress/issue-203.js index 8b6d360a..94f43b4f 100644 --- a/test/compress/issue-203.js +++ b/test/compress/issue-203.js @@ -8,7 +8,7 @@ compress_new_function: { new Function("aa, bb", 'return aa;'); } expect: { - Function("a", "b", "return a"); + Function("n", "r", "return n"); } } @@ -27,9 +27,9 @@ compress_new_function_with_destruct: { new Function("[[aa]], [{bb}]", 'return aa;'); } expect: { - Function("a", "[b]", "return a"); - Function("a", "{bb:b}", "return a"); - Function("[[a]]", "[{bb:b}]", 'return a'); + Function("n", "[r]", "return n"); + Function("n", "{bb:b}", "return n"); + Function("[[n]]", "[{bb:b}]", "return n"); } } diff --git a/test/compress/issue-747.js b/test/compress/issue-747.js index 0a4e4502..1f7079c2 100644 --- a/test/compress/issue-747.js +++ b/test/compress/issue-747.js @@ -1,37 +1,41 @@ dont_reuse_prop: { mangle_props = { regex: /asd/ - }; - + } input: { + "aaaaaaaaaabbbbb"; var obj = {}; obj.a = 123; obj.asd = 256; console.log(obj.a); } expect: { + "aaaaaaaaaabbbbb"; var obj = {}; obj.a = 123; obj.b = 256; console.log(obj.a); } + expect_stdout: "123" } unmangleable_props_should_always_be_reserved: { mangle_props = { regex: /asd/ - }; - + } input: { + "aaaaaaaaaabbbbb"; var obj = {}; obj.asd = 256; obj.a = 123; console.log(obj.a); } expect: { + "aaaaaaaaaabbbbb"; var obj = {}; obj.b = 256; obj.a = 123; console.log(obj.a); } -} \ No newline at end of file + expect_stdout: "123" +} diff --git a/test/compress/object.js b/test/compress/object.js index 19ee2adc..5ac9394e 100644 --- a/test/compress/object.js +++ b/test/compress/object.js @@ -105,7 +105,7 @@ getter_setter_mangler: { }; } } - expect_exact: "function f(n,t){return{get:n,set:t,get g(){},set s(n){},c,a:1,m(){}}}" + expect_exact: "function f(t,e){return{get:t,set:e,get g(){},set s(t){},c,a:1,m(){}}}" } use_shorthand_opportunity: { @@ -297,7 +297,7 @@ concise_methods_and_mangle_props: { expect: { function x() { obj = { - a() { return 1; } + o() { return 1; } } } } diff --git a/test/compress/properties.js b/test/compress/properties.js index b7e63933..c0c80ba2 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -135,11 +135,11 @@ mangle_properties: { a['run']({color: "blue", foo: "baz"}); } expect: { - a["a"] = "bar"; - a.b = "red"; - x = {c: 10}; - a.d(x.c, a.a); - a['d']({b: "blue", a: "baz"}); + a["o"] = "bar"; + a.a = "red"; + x = {r: 10}; + a.b(x.r, a.o); + a['b']({a: "blue", o: "baz"}); } } @@ -178,16 +178,16 @@ mangle_unquoted_properties: { function f1() { a["foo"] = "bar"; a.color = "red"; - a.b = 2; - x = {"bar": 10, c: 7}; - a.c = 9; + a.o = 2; + x = {"bar": 10, f: 7}; + a.f = 9; } function f2() { a.foo = "bar"; a['color'] = "red"; - x = {bar: 10, c: 7}; - a.c = 9; - a.b = 3; + x = {bar: 10, f: 7}; + a.f = 9; + a.o = 3; } } } diff --git a/test/compress/pure_getters.js b/test/compress/pure_getters.js index 81a96b73..dc56e19d 100644 --- a/test/compress/pure_getters.js +++ b/test/compress/pure_getters.js @@ -241,3 +241,147 @@ issue_2110_2: { } expect_stdout: "function" } + +set_immutable_1: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + 1..foo += ""; + if (1..foo) console.log("FAIL"); + else console.log("PASS"); + } + expect_stdout: "PASS" +} + +set_immutable_2: { + options = { + cascade: true, + conditionals: true, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + var a = 1; + a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: "PASS" +} + +set_immutable_3: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + "use strict"; + 1..foo += ""; + if (1..foo) console.log("FAIL"); + else console.log("PASS"); + } + expect_stdout: true +} + +set_immutable_4: { + options = { + cascade: true, + conditionals: true, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + "use strict"; + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + "use strict"; + var a = 1; + a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: true +} + +set_mutable_1: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + unused: true, + } + input: { + !function a() { + a.foo += ""; + if (a.foo) console.log("PASS"); + else console.log("FAIL"); + }(); + } + expect: { + !function a() { + if (a.foo += "") console.log("PASS"); + else console.log("FAIL"); + }(); + } + expect_stdout: "PASS" +} + +set_mutable_2: { + options = { + cascade: true, + conditionals: true, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + } + input: { + !function a() { + a.foo += ""; + if (a.foo) console.log("PASS"); + else console.log("FAIL"); + }(); + } + expect: { + !function a() { + (a.foo += "") ? console.log("PASS") : console.log("FAIL"); + }(); + } + expect_stdout: "PASS" +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 576b8b76..2734f5dd 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2625,3 +2625,28 @@ issue_2090_2: { expect_stdout: "1" node_version: ">=4" } + +for_in_prop: { + options = { + reduce_vars: true, + } + input: { + var a = { + foo: function() { + for (this.b in [1, 2]); + } + }; + a.foo(); + console.log(a.b); + } + expect: { + var a = { + foo: function() { + for (this.b in [1, 2]); + } + }; + a.foo(); + console.log(a.b); + } + expect_stdout: "1" +} diff --git a/test/input/invalid/for-in_1.js b/test/input/invalid/for-in_1.js new file mode 100644 index 00000000..4d872d19 --- /dev/null +++ b/test/input/invalid/for-in_1.js @@ -0,0 +1,4 @@ +var a, b = [1, 2]; +for (1, 2, a in b) { + console.log(a, b[a]); +} diff --git a/test/input/invalid/for-in_2.js b/test/input/invalid/for-in_2.js new file mode 100644 index 00000000..57d861e9 --- /dev/null +++ b/test/input/invalid/for-in_2.js @@ -0,0 +1,4 @@ +var c = [1, 2]; +for (var a, b in c) { + console.log(a, c[a]); +} diff --git a/test/mocha/cli.js b/test/mocha/cli.js index 4ba7b7d1..0ad89cb8 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -611,6 +611,36 @@ describe("bin/uglifyjs", function () { done(); }); }); + it("Should throw syntax error (for-in init)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/for-in_1.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/for-in_1.js:2,5", + "for (1, 2, a in b) {", + " ^", + "ERROR: Invalid left-hand side in for..in loop" + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (for-in var)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/for-in_2.js'; + + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/for-in_2.js:2,5", + "for (var a, b in c) {", + " ^", + "ERROR: Only one variable declaration allowed in for..in loop" + ].join("\n")); + done(); + }); + }); it("Should handle literal string as source map input", function(done) { var command = [ uglifyjscmd, diff --git a/test/mocha/let.js b/test/mocha/let.js index f41fd59b..23909986 100644 --- a/test/mocha/let.js +++ b/test/mocha/let.js @@ -2,29 +2,37 @@ var Uglify = require('../../'); var assert = require("assert"); describe("let", function() { - it("Should not produce `let` as a variable name in mangle", function(done) { + it("Should not produce reserved keywords as variable name in mangle", function(done) { this.timeout(10000); // Produce a lot of variables in a function and run it through mangle. - var s = '"use strict"; function foo() {'; - for (var i = 0; i < 21000; ++i) { + var s = '"dddddeeeeelllllooooottttt"; function foo() {'; + for (var i = 0; i < 18000; i++) { s += "var v" + i + "=0;"; } s += '}'; var result = Uglify.minify(s, {compress: false}); // Verify that select keywords and reserved keywords not produced - assert.strictEqual(result.code.indexOf("var let="), -1); - assert.strictEqual(result.code.indexOf("var do="), -1); - assert.strictEqual(result.code.indexOf("var var="), -1); + [ + "do", + "let", + "var", + ].forEach(function(name) { + assert.strictEqual(result.code.indexOf("var " + name + "="), -1); + }); // Verify that the variable names that appeared immediately before - // and after the erroneously generated `let` variable name still exist + // and after the erroneously generated variable name still exist // to show the test generated enough symbols. - assert(result.code.indexOf("var ket=") >= 0); - assert(result.code.indexOf("var met=") >= 0); + [ + "to", "eo", + "eet", "fet", + "rar", "oar", + ].forEach(function(name) { + assert.ok(result.code.indexOf("var " + name + "=") >= 0); + }); done(); }); }); - diff --git a/test/run-tests.js b/test/run-tests.js index 71ffe72a..6b8c9ddf 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -86,7 +86,6 @@ function run_compress_tests() { log_start_file(file); function test_case(test) { log_test(test.name); - U.base54.reset(); var output_options = test.beautify || {}; var expect; if (test.expect) { @@ -101,9 +100,6 @@ function run_compress_tests() { quote_style: 3, keep_quoted_props: true }); - if (test.mangle_props) { - input = U.mangle_properties(input, test.mangle_props); - } var options = U.defaults(test.options, { warnings: false }); @@ -118,10 +114,16 @@ function run_compress_tests() { var cmp = new U.Compressor(options, true); var output = cmp.compress(input); output.figure_out_scope(test.mangle); - if (test.mangle) { + if (test.mangle || test.mangle_props) { + U.base54.reset(); output.compute_char_frequency(test.mangle); + } + if (test.mangle) { output.mangle_names(test.mangle); } + if (test.mangle_props) { + output = U.mangle_properties(output, test.mangle_props); + } output = make_code(output, output_options); if (expect != output) { log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {