diff --git a/README.md b/README.md index c9032861..e8cca3c7 100644 --- a/README.md +++ b/README.md @@ -691,7 +691,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u where `eval` or `with` are used. - `safari10` (default `false`). Pass `true` to work around the Safari 10 loop - iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041) + iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041) "Cannot declare a let variable twice". Examples: diff --git a/lib/compress.js b/lib/compress.js index 367235c3..ca584969 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1718,6 +1718,63 @@ merge(Compressor.prototype, { } throw def; }); + var object_fns = [ + 'constructor', + 'toString', + 'valueOf', + ]; + var native_fns = { + Array: makePredicate([ + 'indexOf', + 'join', + 'lastIndexOf', + 'slice', + ].concat(object_fns)), + Boolean: makePredicate(object_fns), + Number: makePredicate([ + 'toExponential', + 'toFixed', + 'toPrecision', + ].concat(object_fns)), + RegExp: makePredicate([ + 'test', + ].concat(object_fns)), + String: makePredicate([ + 'charAt', + 'charCodeAt', + 'concat', + 'indexOf', + 'italics', + 'lastIndexOf', + 'match', + 'replace', + 'search', + 'slice', + 'split', + 'substr', + 'substring', + 'trim', + ].concat(object_fns)), + }; + def(AST_Call, function(compressor){ + var exp = this.expression; + if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { + var key = exp.property; + if (key instanceof AST_Node) { + key = ev(key, compressor); + } + var val = ev(exp.expression, compressor); + if ((val && native_fns[val.constructor.name] || return_false)(key)) { + return val[key].apply(val, this.args.map(function(arg) { + return ev(arg, compressor); + })); + } + } + throw def; + }); + def(AST_New, function(compressor){ + throw def; + }); })(function(node, func){ node.DEFMETHOD("_eval", func); }); @@ -3312,6 +3369,11 @@ merge(Compressor.prototype, { && is_iife_call(self)) { return self.negate(compressor, true); } + var ev = self.evaluate(compressor); + if (ev !== self) { + ev = make_node_from_constant(ev, self).optimize(compressor); + return best_of(compressor, ev, self); + } return self; }); diff --git a/lib/output.js b/lib/output.js index 5c45629d..415437bd 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1536,7 +1536,8 @@ function OutputStream(options) { DEFPRINT(AST_NewTarget, function(self, output) { output.print("new.target"); }); - AST_ObjectProperty.DEFMETHOD("print_property_name", function(key, quote, output) { + + function print_property_name(key, quote, output) { if (output.option("quote_keys")) { output.print_string(key + ""); } else if ((typeof key == "number" @@ -1553,7 +1554,8 @@ function OutputStream(options) { } else { output.print_string(key, quote); } - }); + } + DEFPRINT(AST_ObjectKeyVal, function(self, output){ function get_name(self) { var def = self.definition(); @@ -1566,7 +1568,7 @@ function OutputStream(options) { is_identifier_string(self.key) && get_name(self.value) === self.key ) { - self.print_property_name(self.key, self.quote, output); + print_property_name(self.key, self.quote, output); } else if (allowShortHand && self.value instanceof AST_DefaultAssign && @@ -1574,12 +1576,12 @@ function OutputStream(options) { is_identifier_string(self.key) && get_name(self.value.left) === self.key ) { - self.print_property_name(self.key, self.quote, output); + print_property_name(self.key, self.quote, output); output.print("="); self.value.right.print(output); } else { if (!(self.key instanceof AST_Node)) { - self.print_property_name(self.key, self.quote, output); + print_property_name(self.key, self.quote, output); } else { output.with_square(function() { self.key.print(output); @@ -1589,15 +1591,18 @@ function OutputStream(options) { self.value.print(output); } }); - AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) { + AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) { + var self = this; if (self.static) { output.print("static"); output.space(); } - output.print(type); - output.space(); + if (type) { + output.print(type); + output.space(); + } if (self.key instanceof AST_SymbolMethod) { - self.print_property_name(self.key.name, self.quote, output); + print_property_name(self.key.name, self.quote, output); } else { output.with_square(function() { self.key.print(output); @@ -1606,27 +1611,13 @@ function OutputStream(options) { self.value._do_print(output, true); }); DEFPRINT(AST_ObjectSetter, function(self, output){ - self._print_getter_setter("set", self, output); + self._print_getter_setter("set", output); }); DEFPRINT(AST_ObjectGetter, function(self, output){ - self._print_getter_setter("get", self, output); + self._print_getter_setter("get", output); }); DEFPRINT(AST_ConciseMethod, function(self, output){ - if (self.static) { - output.print("static"); - output.space(); - } - if (self.is_generator) { - output.print("*"); - } - if (self.key instanceof AST_SymbolMethod) { - self.print_property_name(self.key.name, self.quote, output); - } else { - output.with_square(function() { - self.key.print(output); - }); - } - self.value._do_print(output, true); + self._print_getter_setter(self.is_generator && "*", output); }); AST_Symbol.DEFMETHOD("_do_print", function(output){ var def = this.definition(); diff --git a/package.json b/package.json index d96e68e6..3a0f17d9 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.14", + "version": "3.0.15", "engines": { "node": ">=0.8.0" }, diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 21b595e9..5810ab87 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -873,13 +873,15 @@ unsafe_charAt_noop: { input: { console.log( s.charAt(0), - "string".charAt(x) + "string".charAt(x), + (typeof x).charAt() ); } expect: { console.log( s.charAt(0), - "string".charAt(x) + "string".charAt(x), + (typeof x)[0] ); } } @@ -1130,3 +1132,31 @@ issue_1964_2: { } expect_stdout: "b" } + +array_slice_index: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log([1,2,3].slice(1)[1]); + } + expect: { + console.log(3); + } + expect_stdout: "3" +} + +string_charCodeAt: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log("foo".charCodeAt("bar".length)); + } + expect: { + console.log(NaN); + } + expect_stdout: "NaN" +} diff --git a/test/compress/properties.js b/test/compress/properties.js index 26a5b06c..6468ff60 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -557,3 +557,20 @@ native_prototype: { "".indexOf.call(e, "bar"); } } + +issue_2040: { + input: { + var a = 1; + var b = { + get "a-b"() { + return a; + }, + set "a-b"(c) { + a = c; + } + }; + console.log(b["a-b"], b["a-b"] = 2, b["a-b"]); + } + expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);' + expect_stdout: "1 2 2" +}