diff --git a/README.md b/README.md index 7a1fe3f7..ba7541bd 100644 --- a/README.md +++ b/README.md @@ -730,7 +730,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u compressor from discarding function names. Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle). -- `passes` -- default `1`. Number of times to run compress with a maximum of 3. +- `passes` -- default `1`. The maximum number of times to run compress. In some cases more than one pass leads to further compressed code. Keep in mind more passes will take more time. diff --git a/bin/uglifyjs b/bin/uglifyjs index 16424386..771e3184 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -35,11 +35,11 @@ else if (process.argv.indexOf("options") >= 0) program.helpInformation = functio } return text.join("\n"); }; -program.option("-p, --parse ", "Specify parser options.", parse_js("parse", true)); -program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); -program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); -program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js("mangle-props", true)); -program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js("beautify", true)); +program.option("-p, --parse ", "Specify parser options.", parse_js()); +program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js()); +program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js()); +program.option("--mangle-props [options]", "Mangle properties/specify mangler options.", parse_js()); +program.option("-b, --beautify [options]", "Beautify output/specify output options.", parse_js()); program.option("-o, --output ", "Output file (default STDOUT)."); program.option("--comments [filter]", "Preserve copyright comments in the output."); program.option("--config-file ", "Read minify() options from JSON file."); @@ -315,7 +315,7 @@ function read_file(path, default_value) { } } -function parse_js(flag, constants) { +function parse_js(flag) { return function(value, options) { options = options || {}; try { @@ -333,7 +333,7 @@ function parse_js(flag, constants) { if (node instanceof UglifyJS.AST_Assign) { var name = node.left.print_to_string(); var value = node.right; - if (!constants) { + if (flag) { options[name] = value; } else if (value instanceof UglifyJS.AST_Array) { options[name] = value.elements.map(to_string); @@ -356,14 +356,18 @@ function parse_js(flag, constants) { } })); } catch(ex) { - options[value] = null; + if (flag) { + fatal("Error parsing arguments for '" + flag + "': " + value); + } else { + options[value] = null; + } } return options; } } function parse_source_map() { - var parse = parse_js("sourceMap", true); + var parse = parse_js(); return function(value, options) { var hasContent = options && "content" in options; var settings = parse(value, options); diff --git a/lib/compress.js b/lib/compress.js index 3500ed87..78e48c96 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -151,10 +151,20 @@ merge(Compressor.prototype, { node.process_expression(true); } var passes = +this.options.passes || 1; - for (var pass = 0; pass < passes && pass < 3; ++pass) { + var last_count = 1 / 0; + for (var pass = 0; pass < passes; pass++) { if (pass > 0 || this.option("reduce_vars")) node.reset_opt_flags(this, true); node = node.transform(this); + if (passes > 1) { + var count = 0; + node.walk(new TreeWalker(function() { + count++; + })); + this.info("pass " + pass + ": last_count: " + last_count + ", count: " + count); + if (count >= last_count) break; + last_count = count; + } } if (this.option("expression")) { node.process_expression(false); @@ -714,6 +724,16 @@ merge(Compressor.prototype, { return false; } + function is_undeclared_ref(node) { + return node instanceof AST_SymbolRef && node.definition().undeclared; + } + + var global_names = makePredicate("Array Boolean console Error Function Math Number RegExp Object String"); + AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { + return !this.definition().undeclared + || compressor.option("unsafe") && global_names(this.name); + }); + function tighten_body(statements, compressor) { var CHANGED, max_iter = 10; do { @@ -785,7 +805,7 @@ merge(Compressor.prototype, { || node instanceof AST_Debugger || node instanceof AST_Destructuring || node instanceof AST_IterationStatement && !(node instanceof AST_For) - || node instanceof AST_SymbolRef && node.undeclared() + || node instanceof AST_SymbolRef && !node.is_declared(compressor) || node instanceof AST_Try || node instanceof AST_With || parent instanceof AST_For && node !== parent.init) { @@ -817,6 +837,7 @@ merge(Compressor.prototype, { right: candidate.value }); } + candidate.write_only = false; return candidate; } // These node types have child nodes that execute sequentially, @@ -1389,12 +1410,12 @@ merge(Compressor.prototype, { // returns true if this node may be null, undefined or contain `AST_Accessor` (function(def) { AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) { - var pure_getters = compressor.option("pure_getters"); - return !pure_getters || this._throw_on_access(pure_getters); + return !compressor.option("pure_getters") + || this._dot_throw(compressor); }); - function is_strict(pure_getters) { - return /strict/.test(pure_getters); + function is_strict(compressor) { + return /strict/.test(compressor.option("pure_getters")); } def(AST_Node, is_strict); @@ -1402,8 +1423,8 @@ merge(Compressor.prototype, { def(AST_Undefined, return_true); def(AST_Constant, return_false); def(AST_Array, return_false); - def(AST_Object, function(pure_getters) { - if (!is_strict(pure_getters)) return false; + def(AST_Object, function(compressor) { + if (!is_strict(compressor)) return false; for (var i = this.properties.length; --i >=0;) if (this.properties[i].value instanceof AST_Accessor) return true; return false; @@ -1414,37 +1435,38 @@ merge(Compressor.prototype, { def(AST_UnaryPrefix, function() { return this.operator == "void"; }); - def(AST_Binary, function(pure_getters) { + def(AST_Binary, function(compressor) { switch (this.operator) { case "&&": - return this.left._throw_on_access(pure_getters); + return this.left._dot_throw(compressor); case "||": - return this.left._throw_on_access(pure_getters) - && this.right._throw_on_access(pure_getters); + return this.left._dot_throw(compressor) + && this.right._dot_throw(compressor); default: return false; } }) - def(AST_Assign, function(pure_getters) { + def(AST_Assign, function(compressor) { return this.operator == "=" - && this.right._throw_on_access(pure_getters); + && this.right._dot_throw(compressor); }) - def(AST_Conditional, function(pure_getters) { - return this.consequent._throw_on_access(pure_getters) - || this.alternative._throw_on_access(pure_getters); + def(AST_Conditional, function(compressor) { + return this.consequent._dot_throw(compressor) + || this.alternative._dot_throw(compressor); }) - def(AST_Sequence, function(pure_getters) { - return this.expressions[this.expressions.length - 1]._throw_on_access(pure_getters); + def(AST_Sequence, function(compressor) { + return this.expressions[this.expressions.length - 1]._dot_throw(compressor); }); - def(AST_SymbolRef, function(pure_getters) { + def(AST_SymbolRef, function(compressor) { if (this.is_undefined) return true; - if (!is_strict(pure_getters)) return false; + if (!is_strict(compressor)) return false; + if (is_undeclared_ref(this) && this.is_declared(compressor)) return false; if (this.is_immutable()) return false; var fixed = this.fixed_value(); - return !fixed || fixed._throw_on_access(pure_getters); + return !fixed || fixed._dot_throw(compressor); }); })(function(node, func) { - node.DEFMETHOD("_throw_on_access", func); + node.DEFMETHOD("_dot_throw", func); }); /* -----[ boolean/negation helpers ]----- */ @@ -1814,11 +1836,8 @@ merge(Compressor.prototype, { }); var global_objs = { Array: Array, - Boolean: Boolean, Math: Math, Number: Number, - RegExp: RegExp, - Object: Object, String: String, }; function convert_to_predicate(obj) { @@ -1855,7 +1874,7 @@ merge(Compressor.prototype, { } var exp = this.expression; var val; - if (exp instanceof AST_SymbolRef && exp.undeclared()) { + if (is_undeclared_ref(exp)) { if (!(static_values[exp.name] || return_false)(key)) return this; val = global_objs[exp.name]; } else { @@ -1932,10 +1951,6 @@ merge(Compressor.prototype, { "isFinite", "isNaN", ], - Object: [ - "keys", - "getOwnPropertyNames", - ], String: [ "fromCharCode", ], @@ -1951,7 +1966,7 @@ merge(Compressor.prototype, { } var val; var e = exp.expression; - if (e instanceof AST_SymbolRef && e.undeclared()) { + if (is_undeclared_ref(e)) { if (!(static_fns[e.name] || return_false)(key)) return this; val = global_objs[e.name]; } else { @@ -2139,7 +2154,7 @@ merge(Compressor.prototype, { || this.expression.has_side_effects(compressor); }); def(AST_SymbolRef, function(compressor){ - return this.undeclared(); + return !this.is_declared(compressor); }); def(AST_SymbolDeclaration, return_false); def(AST_Object, function(compressor){ @@ -2271,7 +2286,12 @@ merge(Compressor.prototype, { if (self.uses_eval || self.uses_with) return; var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs; var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars; - var assign_as_unused = !/keep_assign/.test(compressor.option("unused")); + var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node) { + if (node instanceof AST_Assign && (node.write_only || node.operator == "=")) { + return node.left; + } + if (node instanceof AST_Unary && node.write_only) return node.expression; + }; var in_use = []; var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use if (self instanceof AST_Toplevel && compressor.top_retain) { @@ -2335,13 +2355,9 @@ merge(Compressor.prototype, { }); return true; } - if (assign_as_unused - && node instanceof AST_Assign - && node.operator == "=" - && node.left instanceof AST_SymbolRef - && !is_ref_of(node.left, AST_SymbolBlockDeclaration) - && scope === self) { - node.right.walk(tw); + if (assign_as_unused(node) instanceof AST_SymbolRef && scope === self + && !is_ref_of(node.left, AST_SymbolBlockDeclaration)) { + if (node instanceof AST_Assign) node.right.walk(tw); return true; } if (node instanceof AST_SymbolRef) { @@ -2524,15 +2540,18 @@ merge(Compressor.prototype, { }); } } - if (assign_as_unused - && node instanceof AST_Assign - && node.operator == "=" - && node.left instanceof AST_SymbolRef) { - var def = node.left.definition(); - if (!(def.id in in_use_ids) + if (drop_vars) { + var def = assign_as_unused(node); + if (def instanceof AST_SymbolRef + && !((def = def.definition()).id in in_use_ids) && (drop_vars || !def.global) && self.variables.get(def.name) === def) { - return maintain_this_binding(parent, node, node.right.transform(tt)); + if (node instanceof AST_Assign) { + return maintain_this_binding(parent, node, node.right.transform(tt)); + } + return make_node(AST_Number, node, { + value: 0 + }); } } // certain combination of unused name + side effect leads to: @@ -2785,7 +2804,10 @@ merge(Compressor.prototype, { return make_sequence(this, [ left, right ]); } }); - def(AST_Assign, return_this); + def(AST_Assign, function(compressor){ + this.write_only = !this.left.has_side_effects(compressor); + return this; + }); def(AST_Conditional, function(compressor){ var consequent = this.consequent.drop_side_effect_free(compressor); var alternative = this.alternative.drop_side_effect_free(compressor); @@ -2806,7 +2828,10 @@ merge(Compressor.prototype, { return node; }); def(AST_Unary, function(compressor, first_in_statement){ - if (unary_side_effects(this.operator)) return this; + if (unary_side_effects(this.operator)) { + this.write_only = !this.expression.has_side_effects(compressor); + return this; + } if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return null; var expression = this.expression.drop_side_effect_free(compressor, first_in_statement); if (first_in_statement @@ -2820,8 +2845,8 @@ merge(Compressor.prototype, { } return expression; }); - def(AST_SymbolRef, function() { - return this.undeclared() ? this : null; + def(AST_SymbolRef, function(compressor) { + return this.is_declared(compressor) ? null : this; }); def(AST_Object, function(compressor, first_in_statement){ var values = trim(this.properties, compressor, first_in_statement); @@ -3316,7 +3341,7 @@ merge(Compressor.prototype, { self.args.length = last; } if (compressor.option("unsafe")) { - if (exp instanceof AST_SymbolRef && exp.undeclared()) { + if (is_undeclared_ref(exp)) { switch (exp.name) { case "Array": if (self.args.length != 1) { @@ -3447,8 +3472,7 @@ merge(Compressor.prototype, { } } if (compressor.option("unsafe_Func") - && exp instanceof AST_SymbolRef - && exp.undeclared() + && is_undeclared_ref(exp) && exp.name == "Function") { // new Function() => function(){} if (self.args.length == 0) return make_node(AST_Function, self, { @@ -3581,9 +3605,7 @@ merge(Compressor.prototype, { while (name.expression) { name = name.expression; } - if (name instanceof AST_SymbolRef - && name.name == "console" - && name.undeclared()) { + if (is_undeclared_ref(name) && name.name == "console") { return make_node(AST_Undefined, self).optimize(compressor); } } @@ -3604,7 +3626,7 @@ merge(Compressor.prototype, { OPT(AST_New, function(self, compressor){ if (compressor.option("unsafe")) { var exp = self.expression; - if (exp instanceof AST_SymbolRef && exp.undeclared()) { + if (is_undeclared_ref(exp)) { switch (exp.name) { case "Object": case "RegExp": @@ -3681,6 +3703,8 @@ merge(Compressor.prototype, { operator: car.operator, expression: left }); + } else { + car.write_only = false; } if (parent) { parent[field] = car; @@ -3896,7 +3920,7 @@ merge(Compressor.prototype, { && self.right instanceof AST_UnaryPrefix && self.right.operator == "typeof") { var expr = self.right.expression; - if (expr instanceof AST_SymbolRef ? !expr.undeclared() + if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor) : !(expr instanceof AST_PropAccess && compressor.option("ie8"))) { self.right = expr; self.left = make_node(AST_Undefined, self.left).optimize(compressor); @@ -4226,7 +4250,7 @@ merge(Compressor.prototype, { } // testing against !self.scope.uses_with first is an optimization if (!compressor.option("ie8") - && self.undeclared() + && is_undeclared_ref(self) && (!self.scope.uses_with || !compressor.find_parent(AST_With))) { switch (self.name) { case "undefined": @@ -4598,7 +4622,7 @@ merge(Compressor.prototype, { var prop = self.property; if (prop instanceof AST_String && compressor.option("properties")) { prop = prop.getValue(); - if (RESERVED_WORDS(prop) ? !compressor.option("ie8") : is_identifier_string(prop)) { + if (is_identifier_string(prop)) { return make_node(AST_Dot, self, { expression : self.expression, property : prop @@ -4635,20 +4659,11 @@ merge(Compressor.prototype, { if (def) { return def.optimize(compressor); } - var prop = self.property; - if (RESERVED_WORDS(prop) && compressor.option("ie8")) { - return make_node(AST_Sub, self, { - expression : self.expression, - property : make_node(AST_String, self, { - value: prop - }) - }).optimize(compressor); - } if (compressor.option("unsafe") && self.expression instanceof AST_Object) { var values = self.expression.properties; for (var i = values.length; --i >= 0;) { var key = values[i].key; - if ((key instanceof AST_SymbolMethod ? key.name : key) === prop) { + if ((key instanceof AST_SymbolMethod ? key.name : key) === self.property) { var value = values[i].value; if (key instanceof AST_SymbolMethod) { if (values[i].is_generator) break; @@ -4667,7 +4682,7 @@ merge(Compressor.prototype, { && self.expression instanceof AST_Dot && self.expression.property == "prototype") { var exp = self.expression.expression; - if (exp instanceof AST_SymbolRef && exp.undeclared()) switch (exp.name) { + if (is_undeclared_ref(exp)) switch (exp.name) { case "Array": self.expression = make_node(AST_Array, self.expression, { elements: [] diff --git a/lib/output.js b/lib/output.js index 7df56a66..7ab562c6 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1400,6 +1400,9 @@ function OutputStream(options) { self.expression.print(output); if (self instanceof AST_New && !need_constructor_parens(self, output)) return; + if (self.expression instanceof AST_Lambda) { + output.add_mapping(self.start); + } output.with_parens(function(){ self.args.forEach(function(expr, i){ if (i) output.comma(); @@ -1439,15 +1442,23 @@ function OutputStream(options) { DEFPRINT(AST_Dot, function(self, output){ var expr = self.expression; expr.print(output); - if (expr instanceof AST_Number && expr.getValue() >= 0) { - if (!/[xa-f.)]/i.test(output.last())) { - output.print("."); + var prop = self.property; + if (output.option("ie8") && RESERVED_WORDS(prop)) { + output.print("["); + output.add_mapping(self.end); + output.print_string(prop); + output.print("]"); + } else { + if (expr instanceof AST_Number && expr.getValue() >= 0) { + if (!/[xa-f.)]/i.test(output.last())) { + output.print("."); + } } + output.print("."); + // the name after dot would be mapped about here. + output.add_mapping(self.end); + output.print_name(prop); } - output.print("."); - // the name after dot would be mapped about here. - output.add_mapping(self.end); - output.print_name(self.property); }); DEFPRINT(AST_Sub, function(self, output){ self.expression.print(output); diff --git a/lib/propmangle.js b/lib/propmangle.js index fae64cd7..fd88eb5b 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -94,7 +94,7 @@ function mangle_properties(ast, options) { only_cache: false, regex: null, reserved: null, - }); + }, true); var reserved = options.reserved; if (!Array.isArray(reserved)) reserved = []; diff --git a/lib/scope.js b/lib/scope.js index ed0f0a37..1beeaa64 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -461,14 +461,6 @@ AST_Symbol.DEFMETHOD("unreferenced", function(){ && !(this.scope.uses_eval || this.scope.uses_with); }); -AST_Symbol.DEFMETHOD("undeclared", function(){ - return this.definition().undeclared; -}); - -AST_LabelRef.DEFMETHOD("undeclared", return_false); - -AST_Label.DEFMETHOD("undeclared", return_false); - AST_Symbol.DEFMETHOD("definition", function(){ return this.thedef; }); diff --git a/package.json b/package.json index 951a3ed7..3ff52057 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.24", + "version": "3.0.25", "engines": { "node": ">=0.8.0" }, diff --git a/test/benchmark.js b/test/benchmark.js index e34f0858..8b20ec13 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -6,6 +6,7 @@ var createHash = require("crypto").createHash; var fetch = require("./fetch"); var fork = require("child_process").fork; +var zlib = require("zlib"); var args = process.argv.slice(2); if (!args.length) { args.push("-mc"); @@ -33,6 +34,7 @@ function done() { console.log(info.log); console.log("Original:", info.input, "bytes"); console.log("Uglified:", info.output, "bytes"); + console.log("GZipped: ", info.gzip, "bytes"); console.log("SHA1 sum:", info.sha1); if (info.code) { failures.push(url); @@ -51,6 +53,7 @@ urls.forEach(function(url) { results[url] = { input: 0, output: 0, + gzip: 0, log: "" }; fetch(url, function(err, res) { @@ -61,6 +64,10 @@ urls.forEach(function(url) { }).pipe(uglifyjs.stdin); uglifyjs.stdout.on("data", function(data) { results[url].output += data.length; + }).pipe(zlib.createGzip({ + level: zlib.Z_BEST_COMPRESSION + })).on("data", function(data) { + results[url].gzip += data.length; }).pipe(createHash("sha1")).on("data", function(data) { results[url].sha1 = data.toString("hex"); done(); diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index bd580541..5b64c6a3 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -863,7 +863,7 @@ collapse_vars_unary: { input: { function f0(o, p) { var x = o[p]; - delete x; + return delete x; } function f1(n) { var k = !!n; @@ -893,7 +893,7 @@ collapse_vars_unary: { expect: { function f0(o, p) { var x = o[p]; - delete x; + return delete x; } function f1(n) { return n > +!!n diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index e1536652..e845d667 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -377,3 +377,82 @@ accessor: { } expect: {} } + +issue_2233_1: { + options = { + pure_getters: "strict", + side_effects: true, + unsafe: true, + } + input: { + Array.isArray; + Boolean; + console.log; + Error.name; + Function.length; + Math.random; + Number.isNaN; + RegExp; + Object.defineProperty; + String.fromCharCode; + } + expect: {} + expect_stdout: true +} + +issue_2233_2: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var RegExp; + Array.isArray; + RegExp; + UndeclaredGlobal; + function foo() { + var Number; + AnotherUndeclaredGlobal; + Math.sin; + Number.isNaN; + } + } + expect: { + var RegExp; + UndeclaredGlobal; + function foo() { + var Number; + AnotherUndeclaredGlobal; + Number.isNaN; + } + } +} + +issue_2233_3: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var RegExp; + Array.isArray; + RegExp; + UndeclaredGlobal; + function foo() { + var Number; + AnotherUndeclaredGlobal; + Math.sin; + Number.isNaN; + } + } + expect: { + UndeclaredGlobal; + } +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index c08d5784..5d85328d 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1210,6 +1210,7 @@ var_catch_toplevel: { a--; try { a++; + x(); } catch(a) { if (a) var a; var a = 10; @@ -1219,9 +1220,8 @@ var_catch_toplevel: { } expect: { !function() { - a--; try { - a++; + x(); } catch(a) { var a; } @@ -1427,3 +1427,89 @@ issue_2163: { b; } } + +issue_2226_1: { + options = { + side_effects: true, + unused: true, + } + input: { + function f1() { + var a = b; + a += c; + } + function f2(a) { + a <<= b; + } + function f3(a) { + --a; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } + expect: { + function f1() { + b; + c; + } + function f2(a) { + b; + } + function f3(a) { + 0; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } +} + +issue_2226_2: { + options = { + cascade: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a, b) { + return a += b; + }(1, 2)); + } + expect_stdout: "3" +} + +issue_2226_3: { + options = { + collapse_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a, b) { + return a += 2; + }(1)); + } + expect_stdout: "3" +} diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 67e4ab38..f015c601 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -1250,3 +1250,31 @@ issue_2207_3: { } expect_stdout: true } + +issue_2231_1: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(Object.keys(void 0)); + } + expect: { + console.log(Object.keys(void 0)); + } + expect_stdout: true +} + +issue_2231_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(Object.getOwnPropertyNames(null)); + } + expect: { + console.log(Object.getOwnPropertyNames(null)); + } + expect_stdout: true +} diff --git a/test/compress/issue-1034.js b/test/compress/issue-1034.js index 28e47f07..f312408c 100644 --- a/test/compress/issue-1034.js +++ b/test/compress/issue-1034.js @@ -71,11 +71,13 @@ non_hoisted_function_after_return_2a: { "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:51,16]", "WARN: Dropping unused variable a [test/compress/issue-1034.js:48,20]", "WARN: Dropping unused function nope [test/compress/issue-1034.js:55,21]", + "WARN: pass 0: last_count: Infinity, count: 37", "WARN: Dropping unreachable code [test/compress/issue-1034.js:53,12]", "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:53,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:56,12]", "WARN: Dropping unused variable b [test/compress/issue-1034.js:51,20]", "WARN: Dropping unused variable c [test/compress/issue-1034.js:53,16]", + "WARN: pass 1: last_count: 37, count: 18", ] } @@ -109,11 +111,11 @@ non_hoisted_function_after_return_2b: { } expect_warnings: [ // duplicate warnings no longer emitted - "WARN: Dropping unreachable code [test/compress/issue-1034.js:95,16]", - "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:95,16]", - "WARN: Dropping unreachable code [test/compress/issue-1034.js:97,12]", - "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,12]", - "WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:97,16]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:97,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:99,12]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:99,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:103,12]", ] } @@ -151,10 +153,10 @@ non_hoisted_function_after_return_strict: { } expect_stdout: "8 7" expect_warnings: [ - 'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]', - "WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]", - "WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]", - "WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]" + "WARN: Dropping unreachable code [test/compress/issue-1034.js:133,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:136,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:139,12]", + "WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:140,21]", ] } @@ -194,17 +196,19 @@ non_hoisted_function_after_return_2a_strict: { } expect_stdout: "5 6" expect_warnings: [ - "WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]", - "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]", - "WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]", - "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]", - "WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]", - "WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]", - "WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]", - "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]", - "WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]", - "WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]", - "WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:175,16]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:175,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:178,16]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,16]", + "WARN: Dropping unused variable a [test/compress/issue-1034.js:175,20]", + "WARN: Dropping unused function nope [test/compress/issue-1034.js:182,21]", + "WARN: pass 0: last_count: Infinity, count: 48", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:180,12]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:180,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:183,12]", + "WARN: Dropping unused variable b [test/compress/issue-1034.js:178,20]", + "WARN: Dropping unused variable c [test/compress/issue-1034.js:180,16]", + "WARN: pass 1: last_count: 48, count: 29", ] } @@ -243,10 +247,10 @@ non_hoisted_function_after_return_2b_strict: { expect_stdout: "5 6" expect_warnings: [ // duplicate warnings no longer emitted - "WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]", - "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]", - "WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]", - "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:229,16]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:229,16]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]", + "WARN: Declarations in unreachable code! [test/compress/issue-1034.js:231,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:235,12]", ] } diff --git a/test/compress/properties.js b/test/compress/properties.js index 13c34d58..214135a2 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -13,8 +13,10 @@ keep_properties: { dot_properties: { options = { properties: true, + } + beautify = { ie8: true, - }; + } input: { a["foo"] = "bar"; a["if"] = "if"; @@ -36,8 +38,10 @@ dot_properties: { dot_properties_es5: { options = { properties: true, + } + beautify = { ie8: false, - }; + } input: { a["foo"] = "bar"; a["if"] = "if"; diff --git a/test/mocha/cli.js b/test/mocha/cli.js index 0ad89cb8..40cb7044 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -66,7 +66,7 @@ describe("bin/uglifyjs", function () { if (err) throw err; assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" + - "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n"); + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==\n"); done(); }); }); @@ -195,7 +195,7 @@ describe("bin/uglifyjs", function () { assert.strictEqual(stdout, [ "var bar=function(){function foo(bar){return bar}return foo}();", - "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==", "", ].join("\n")); assert.strictEqual(stderr, "WARN: inline source map not found\n"); @@ -686,8 +686,8 @@ describe("bin/uglifyjs", function () { done(); }); }); - it("Should work with --mangle reserved=[]", function (done) { - var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=[callback]'; + it("Should work with --mangle reserved=[]", function(done) { + var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=[callback]"; exec(command, function (err, stdout) { if (err) throw err; @@ -696,8 +696,8 @@ describe("bin/uglifyjs", function () { done(); }); }); - it("Should work with --mangle reserved=false", function (done) { - var command = uglifyjscmd + ' test/input/issue-505/input.js -m reserved=false'; + it("Should work with --mangle reserved=false", function(done) { + var command = uglifyjscmd + " test/input/issue-505/input.js -m reserved=false"; exec(command, function (err, stdout) { if (err) throw err; @@ -706,4 +706,22 @@ describe("bin/uglifyjs", function () { done(); }); }); + it("Should fail with --mangle-props reserved=[in]", function(done) { + var command = uglifyjscmd + " test/input/issue-505/input.js --mangle-props reserved=[in]"; + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `reserved=\[in]` is not a supported option/.test(stderr), stderr); + done(); + }); + }); + it("Should fail with --define a-b", function(done) { + var command = uglifyjscmd + " test/input/issue-505/input.js --define a-b"; + exec(command, function (err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr, "Error parsing arguments for 'define': a-b\n"); + done(); + }); + }); }); diff --git a/test/mocha/let.js b/test/mocha/let.js index 23909986..8685746b 100644 --- a/test/mocha/let.js +++ b/test/mocha/let.js @@ -2,16 +2,17 @@ var Uglify = require('../../'); var assert = require("assert"); describe("let", function() { - it("Should not produce reserved keywords as variable name in mangle", function(done) { - this.timeout(10000); - + this.timeout(30000); + it("Should not produce reserved keywords as variable name in mangle", function() { // Produce a lot of variables in a function and run it through mangle. 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}); + var result = Uglify.minify(s, { + compress: false + }).code; // Verify that select keywords and reserved keywords not produced [ @@ -19,7 +20,7 @@ describe("let", function() { "let", "var", ].forEach(function(name) { - assert.strictEqual(result.code.indexOf("var " + name + "="), -1); + assert.strictEqual(result.indexOf("var " + name + "="), -1); }); // Verify that the variable names that appeared immediately before @@ -30,9 +31,27 @@ describe("let", function() { "eet", "fet", "rar", "oar", ].forEach(function(name) { - assert.ok(result.code.indexOf("var " + name + "=") >= 0); + assert.notStrictEqual(result.indexOf("var " + name + "="), -1); + }); + }); + it("Should quote mangled properties that are reserved keywords", function() { + var s = '"rrrrrnnnnniiiiiaaaaa";'; + for (var i = 0; i < 18000; i++) { + s += "v.b" + i + ";"; + } + var result = Uglify.minify(s, { + compress: false, + ie8: true, + mangle: { + properties: true, + } + }).code; + [ + "in", + "var", + ].forEach(function(name) { + assert.notStrictEqual(result.indexOf(name), -1); + assert.notStrictEqual(result.indexOf('v["' + name + '"]'), -1); }); - - done(); }); });