diff --git a/lib/compress.js b/lib/compress.js index ade1913c65..5ea5d00b9c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4062,10 +4062,11 @@ merge(Compressor.prototype, { var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; var defs_by_id = Object.create(null); self.transform(new TreeTransformer(function(node, descend) { - if (node instanceof AST_Assign - && node.operator == "=" - && node.write_only - && can_hoist(node.left, node.right, 1)) { + if (node instanceof AST_Assign) { + if (node.operator != "=") return; + if (!node.write_only) return; + if (node.left.scope !== self) return; + if (!can_hoist(node.left, node.right, 1)) return; descend(node, this); var defs = new Dictionary(); var assignments = []; @@ -4094,17 +4095,9 @@ merge(Compressor.prototype, { })); return make_sequence(node, assignments); } - if (node instanceof AST_Unary - && !unary_side_effects[node.operator] - && node.expression instanceof AST_SymbolRef - && node.expression.definition().id in defs_by_id) { - node = node.clone(); - node.expression = make_node(AST_Object, node, { - properties: [] - }); - return node; - } - if (node instanceof AST_VarDef && can_hoist(node.name, node.value, 0)) { + if (node instanceof AST_Scope) return node === self ? undefined : node; + if (node instanceof AST_VarDef) { + if (!can_hoist(node.name, node.value, 0)) return; descend(node, this); var defs = new Dictionary(); var var_defs = []; @@ -4117,32 +4110,6 @@ merge(Compressor.prototype, { defs_by_id[node.name.definition().id] = defs; return MAP.splice(var_defs); } - if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) { - var defs = defs_by_id[node.expression.definition().id]; - if (defs) { - var def = defs.get(node.getProperty()); - var sym = make_node(AST_SymbolRef, node, { - name: def.name, - scope: node.expression.scope, - thedef: def - }); - sym.reference({}); - return sym; - } - } - - function can_hoist(sym, right, count) { - if (sym.scope !== self) 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 == count) return; - if (def.single_use) return; - if (top_retain(def)) return; - if (sym.fixed_value() !== right) return; - return right instanceof AST_Object; - } function make_sym(sym, key) { var new_var = make_node(AST_SymbolVar, sym, { @@ -4155,6 +4122,43 @@ merge(Compressor.prototype, { return new_var; } })); + self.transform(new TreeTransformer(function(node, descend) { + if (node instanceof AST_PropAccess) { + if (!(node.expression instanceof AST_SymbolRef)) return; + var defs = defs_by_id[node.expression.definition().id]; + if (!defs) return; + var def = defs.get(node.getProperty()); + var sym = make_node(AST_SymbolRef, node, { + name: def.name, + scope: node.expression.scope, + thedef: def + }); + sym.reference({}); + return sym; + } + if (node instanceof AST_Unary) { + if (unary_side_effects[node.operator]) return; + if (!(node.expression instanceof AST_SymbolRef)) return; + if (!(node.expression.definition().id in defs_by_id)) return; + var opt = node.clone(); + opt.expression = make_node(AST_Object, node, { + properties: [] + }); + return opt; + } + })); + + function can_hoist(sym, right, count) { + var def = sym.definition(); + if (def.assignments != count) return; + if (def.direct_access) return; + if (def.escaped.depth == 1) return; + if (def.references.length == count) return; + if (def.single_use) return; + if (top_retain(def)) return; + if (sym.fixed_value() !== right) return; + return right instanceof AST_Object; + } }); // drop_side_effect_free() diff --git a/test/compress/hoist_props.js b/test/compress/hoist_props.js index b055ae7bb5..0d8e771ae6 100644 --- a/test/compress/hoist_props.js +++ b/test/compress/hoist_props.js @@ -886,3 +886,31 @@ issue_3411: { } expect_stdout: "PASS" } + +issue_3440: { + options = { + hoist_props: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f() { + console.log(o.p); + } + var o = { + p: "PASS", + }; + return f; + })()(); + } + expect: { + (function() { + var o_p = "PASS"; + return function() { + console.log(o_p); + }; + })()(); + } + expect_stdout: "PASS" +}