diff --git a/lib/compress.js b/lib/compress.js index 1f2af1fa..f59a9966 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -703,6 +703,7 @@ merge(Compressor.prototype, { if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1; if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth; if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true; + if (d.fixed) d.fixed.escaped = d.escaped; return; } else if (value_in_use(node, parent)) { mark_escaped(tw, d, scope, parent, parent, level + 1, depth); @@ -720,6 +721,7 @@ merge(Compressor.prototype, { if (parent instanceof AST_SimpleStatement) return; if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return; d.direct_access = true; + if (d.fixed) d.fixed.direct_access = true; } function mark_assignment_to_arguments(node) { @@ -1000,9 +1002,9 @@ merge(Compressor.prototype, { d.single_use = false; } tw.loop_ids[d.id] = tw.in_loop; - mark_escaped(tw, d, sym.scope, node, right, 0, 1); sym.fixed = d.fixed = fixed; sym.fixed.assigns = [ node ]; + mark_escaped(tw, d, sym.scope, node, right, 0, 1); } }); } @@ -7678,6 +7680,7 @@ merge(Compressor.prototype, { right: prop.value, })); }); + defs.value = node.right; defs_by_id[node.left.definition().id] = defs; self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, { definitions: decls, @@ -7690,12 +7693,16 @@ merge(Compressor.prototype, { descend(node, this); var defs = new Dictionary(); var var_defs = []; + var decl = node.clone(); + decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null; + var_defs.push(decl); node.value.properties.forEach(function(prop) { var_defs.push(make_node(AST_VarDef, node, { name: make_sym(node.name.CTOR, node.name, prop.key), value: prop.value, })); }); + defs.value = node.value; defs_by_id[node.name.definition().id] = defs; return List.splice(var_defs); } @@ -7711,6 +7718,7 @@ merge(Compressor.prototype, { if (!(node.expression instanceof AST_SymbolRef)) return; var defs = defs_by_id[node.expression.definition().id]; if (!defs) return; + if (node.expression.fixed_value() !== defs.value) return; var def = defs.get(node.get_property()); var sym = make_node(AST_SymbolRef, node, { name: def.name, @@ -7721,7 +7729,9 @@ merge(Compressor.prototype, { return sym; } if (node instanceof AST_SymbolRef) { - if (!(node.definition().id in defs_by_id)) return; + var defs = defs_by_id[node.definition().id]; + if (!defs) return; + if (node.fixed_value() !== defs.value) return; return make_node(AST_Object, node, { properties: [] }); } })); @@ -7730,18 +7740,16 @@ merge(Compressor.prototype, { if (!(sym instanceof AST_Symbol)) return; var def = sym.definition(); if (def.assignments != count) return; - if (def.direct_access) return; - if (def.escaped.depth == 1) return; if (def.references.length - def.replaced == count) return; if (def.single_use) return; if (top_retain(def)) return; if (sym.fixed_value() !== right) return; + var fixed = sym.fixed || def.fixed; + if (fixed.direct_access) return; + if (fixed.escaped && fixed.escaped.depth == 1) return; return right instanceof AST_Object && right.properties.length > 0 && all(right.properties, can_hoist_property) - && all(def.references, function(ref) { - return ref.fixed_value() === right; - }) && can_drop_symbol(sym, compressor); } }); diff --git a/test/compress/hoist_props.js b/test/compress/hoist_props.js index 2b4abff1..ab9a16f3 100644 --- a/test/compress/hoist_props.js +++ b/test/compress/hoist_props.js @@ -217,7 +217,8 @@ name_collision_1: { var obj_foo = 1; var obj_bar = 2; function f() { - var obj_foo$0 = 3, + var obj, + obj_foo$0 = 3, obj_bar = 4, obj_b_r = 5, obj_b_r$0 = 6, @@ -249,7 +250,8 @@ name_collision_2: { console.log(o.p === o.p, o["+"](4), o["-"](5), o__$0, o__$1); } expect: { - var o_p = 1, + var o, + o_p = 1, o__ = function(x) { return x; }, @@ -283,7 +285,8 @@ name_collision_3: { console.log(o.p === o.p, o["+"](4), o["-"](5)); } expect: { - var o_p = 1, + var o, + o_p = 1, o__ = function(x) { return x; }, @@ -315,7 +318,7 @@ name_collision_4: { } expect: { console.log(function() { - var o_p$0 = 0, o_q = "PASS"; + var o, o_p$0 = 0, o_q = "PASS"; return function(o_p) { if (!o_p$0) return o_p; }(o_q); @@ -768,7 +771,7 @@ issue_3046: { expect: { console.log(function(a) { do { - var b_c = a++; + var b, b_c = a++; } while (b_c && a); return a; }(0)); @@ -931,7 +934,7 @@ issue_3411: { expect: { var c = 1; !function f() { - var o_p = --c && f(); + var o, o_p = --c && f(); +{} || console.log("PASS"); }(); } @@ -1042,9 +1045,7 @@ issue_3945_1: { expect: { function f() { o.p; - var o = { - q: 0, - }; + var o, o_q = 0; } } } @@ -1063,9 +1064,7 @@ issue_3945_2: { } expect: { console.log(typeof o); - var o = { - p: 0, - }; + var o, o_p = 0; } expect_stdout: "undefined" } @@ -1134,10 +1133,46 @@ issue_4985: { }()); } expect: { - var a_p = 42; + var a, a_p = 42; console.log(function() { ({}); }()); } expect_stdout: "undefined" } + +issue_5182: { + options = { + hoist_props: true, + merge_vars: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var o = console; + log = o.log; + o = { + p: function(a) { + console.log(a ? "PASS" : "FAIL"); + return a; + }, + }; + log(o.p(42)); + } + expect: { + var o_p = console; + log = o_p.log; + o_p = function(a) { + console.log(a ? "PASS" : "FAIL"); + return a; + }; + log(o_p(42)); + } + expect_stdout: [ + "PASS", + "42", + ] +} diff --git a/test/compress/varify.js b/test/compress/varify.js index d8b2bb49..4a90a955 100644 --- a/test/compress/varify.js +++ b/test/compress/varify.js @@ -111,7 +111,7 @@ hoist_props_const: { } } expect: { - var o_p = "PASS"; + var o = 0, o_p = "PASS"; console.log(o_p); } expect_stdout: "PASS" @@ -136,7 +136,7 @@ hoist_props_let: { } expect: { "use strict"; - var o_p = "PASS"; + var o, o_p = "PASS"; console.log(o_p); } expect_stdout: "PASS"