From: Alex Lam S.L Date: Sat, 28 Oct 2017 20:11:26 +0000 (+0800) Subject: enhance `properties` (#2412) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=8428326ea120dece51b70d7bba63dda8eda14fd6;p=UglifyJS.git enhance `properties` (#2412) - trim array items only if `side_effects` - extend to non-identifier properties --- diff --git a/lib/compress.js b/lib/compress.js index 4817ec5f..a1db985c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4537,59 +4537,72 @@ merge(Compressor.prototype, { }); OPT(AST_Sub, function(self, compressor){ + var expr = self.expression; + var prop = self.property; if (compressor.option("properties")) { - var prop = self.property; - if (prop instanceof AST_String) { - prop = prop.getValue(); - if (is_identifier_string(prop)) { + var key = prop.evaluate(compressor); + if (key !== prop) { + var property = "" + key; + if (is_identifier_string(property) + && property.length <= prop.print_to_string().length + 1) { return make_node(AST_Dot, self, { - expression : self.expression, - property : prop + expression: expr, + property: property }).optimize(compressor); } - var v = parseFloat(prop); - if (!isNaN(v) && v.toString() == prop) { - self.property = make_node(AST_Number, self.property, { - value: v - }); + if (!(prop instanceof AST_Number)) { + var value = parseFloat(property); + if (value.toString() == property) { + prop = self.property = make_node(AST_Number, prop, { + value: value + }); + } } } - if (prop instanceof AST_Number && self.expression instanceof AST_Array) { - prop = prop.getValue(); - var elements = self.expression.elements; - if (prop in elements) { - var flatten = true; - var values = []; - for (var i = elements.length; --i > prop;) { - var value = elements[i].drop_side_effect_free(compressor); - if (value) { - values.unshift(value); - if (flatten && value.has_side_effects(compressor)) flatten = false; - } - } - var retValue = elements[prop]; - retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue; - if (!flatten) values.unshift(retValue); - while (--i >= 0) { - var value = elements[i].drop_side_effect_free(compressor); - if (value) values.unshift(value); - else prop--; + } + if (is_lhs(self, compressor.parent())) return self; + if (compressor.option("properties") && key !== prop) { + var node = self.flatten_object(property); + if (node) { + expr = self.expression = node.expression; + prop = self.property = node.property; + } + } + if (compressor.option("properties") && compressor.option("side_effects") + && prop instanceof AST_Number && expr instanceof AST_Array) { + var index = prop.getValue(); + var elements = expr.elements; + if (index in elements) { + var flatten = true; + var values = []; + for (var i = elements.length; --i > index;) { + var value = elements[i].drop_side_effect_free(compressor); + if (value) { + values.unshift(value); + if (flatten && value.has_side_effects(compressor)) flatten = false; } - if (flatten) { - values.push(retValue); - return make_sequence(self, values).optimize(compressor); - } else return make_node(AST_Sub, self, { - expression: make_node(AST_Array, self.expression, { - elements: values - }), - property: make_node(AST_Number, self.property, { - value: prop - }) - }); } + var retValue = elements[index]; + retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue; + if (!flatten) values.unshift(retValue); + while (--i >= 0) { + var value = elements[i].drop_side_effect_free(compressor); + if (value) values.unshift(value); + else index--; + } + if (flatten) { + values.push(retValue); + return make_sequence(self, values).optimize(compressor); + } else return make_node(AST_Sub, self, { + expression: make_node(AST_Array, expr, { + elements: values + }), + property: make_node(AST_Number, prop, { + value: index + }) + }); } } - if (is_lhs(self, compressor.parent())) return self; var ev = self.evaluate(compressor); if (ev !== self) { ev = make_node_from_constant(ev, self).optimize(compressor); @@ -4609,6 +4622,33 @@ merge(Compressor.prototype, { return result; }); + AST_PropAccess.DEFMETHOD("flatten_object", function(key) { + var expr = this.expression; + if (expr instanceof AST_Object) { + var props = expr.properties; + for (var i = props.length; --i >= 0;) { + var prop = props[i]; + if ("" + prop.key == key) { + if (!all(props, function(prop) { + return prop instanceof AST_ObjectKeyVal; + })) break; + var value = prop.value; + if (value instanceof AST_Function && value.contains_this()) break; + return make_node(AST_Sub, this, { + expression: make_node(AST_Array, expr, { + elements: props.map(function(prop) { + return prop.value; + }) + }), + property: make_node(AST_Number, this, { + value: i + }) + }); + } + } + } + }); + OPT(AST_Dot, function(self, compressor){ var def = self.resolve_defines(compressor); if (def) { @@ -4637,28 +4677,9 @@ merge(Compressor.prototype, { } } if (is_lhs(self, compressor.parent())) return self; - if (compressor.option("properties") && self.expression instanceof AST_Object) { - var props = self.expression.properties; - for (var i = props.length; --i >= 0;) { - var prop = props[i]; - if (prop.key === self.property) { - if (!all(props, function(prop) { - return prop instanceof AST_ObjectKeyVal; - })) break; - var value = prop.value; - if (value instanceof AST_Function && value.contains_this()) break; - return make_node(AST_Sub, self, { - expression: make_node(AST_Array, self.expression, { - elements: props.map(function(prop) { - return prop.value; - }) - }), - property: make_node(AST_Number, self, { - value: i - }) - }).optimize(compressor); - } - } + if (compressor.option("properties")) { + var node = self.flatten_object(self.property); + if (node) return node.optimize(compressor); } var ev = self.evaluate(compressor); if (ev !== self) { diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index dc8ceb62..64728c06 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -390,6 +390,7 @@ prop_function: { options = { evaluate: true, properties: true, + side_effects: true, } input: { console.log( @@ -634,6 +635,7 @@ prototype_function: { options = { evaluate: true, properties: true, + side_effects: true, } input: { var a = ({valueOf: 0}) < 1; diff --git a/test/compress/properties.js b/test/compress/properties.js index f435d371..1b5e7fc7 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -1,7 +1,8 @@ keep_properties: { options = { - properties: false - }; + evaluate: true, + properties: false, + } input: { a["foo"] = "bar"; } @@ -12,6 +13,7 @@ keep_properties: { dot_properties: { options = { + evaluate: true, properties: true, } beautify = { @@ -37,6 +39,7 @@ dot_properties: { dot_properties_es5: { options = { + evaluate: true, properties: true, } beautify = { @@ -61,8 +64,8 @@ dot_properties_es5: { sub_properties: { options = { evaluate: true, - properties: true - }; + properties: true, + } input: { a[0] = 0; a["0"] = 1; @@ -81,18 +84,18 @@ sub_properties: { a[3.14] = 3; a.if = 4; a["foo bar"] = 5; - a[NaN] = 6; - a[null] = 7; + a.NaN = 6; + a.null = 7; a[void 0] = 8; } } evaluate_array_length: { options = { + evaluate: true, properties: true, unsafe: true, - evaluate: true - }; + } input: { a = [1, 2, 3].length; a = [1, 2, 3].join()["len" + "gth"]; @@ -109,10 +112,10 @@ evaluate_array_length: { evaluate_string_length: { options = { + evaluate: true, properties: true, unsafe: true, - evaluate: true - }; + } input: { a = "foo".length; a = ("foo" + "bar")["len" + "gth"]; @@ -151,7 +154,8 @@ mangle_properties: { mangle_unquoted_properties: { options = { - properties: false + evaluate: true, + properties: false, } mangle = { properties: { @@ -249,7 +253,8 @@ mangle_debug_suffix: { mangle_debug_suffix_keep_quoted: { options = { - properties: false + evaluate: true, + properties: false, } mangle = { properties: { @@ -833,18 +838,29 @@ lhs_prop_2: { unused: true, } input: { + [1][0] = 42; (function(a) { - a[2] = "g"; + a.b = "g"; })("abc"); + (function(a) { + a[2] = "g"; + })("def"); + (function(a) { + a[""] = "g"; + })("ghi"); } expect: { - "abc"[2] = "g"; + [1][0] = 42; + "abc".b = "g"; + "def"[2] = "g"; + "ghi"[""] = "g"; } } literal_duplicate_key_side_effects: { options = { properties: true, + side_effects: true, } input: { console.log({ @@ -864,6 +880,7 @@ prop_side_effects_1: { inline: true, properties: true, reduce_vars: true, + side_effects: true, toplevel: true, unused: true, } @@ -899,6 +916,7 @@ prop_side_effects_2: { passes: 2, properties: true, reduce_vars: true, + side_effects: true, toplevel: true, unused: true, } @@ -906,11 +924,11 @@ prop_side_effects_2: { var C = 1; console.log(C); var obj = { - bar: function() { + "": function() { return C + C; } }; - console.log(obj.bar()); + console.log(obj[""]()); } expect: { console.log(1); @@ -974,6 +992,7 @@ accessor_2: { array_hole: { options = { properties: true, + side_effects: true, } input: { console.log( diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index ac4fa40b..d7fdee18 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2973,6 +2973,7 @@ obj_var_2: { passes: 2, properties: true, reduce_vars: true, + side_effects: true, toplevel: true, unsafe: true, unused: true,